完善功能

This commit is contained in:
2025-07-16 15:16:40 +08:00
parent b2871ec0d2
commit f68a5b360b
123 changed files with 4643 additions and 931 deletions

View File

@ -11,6 +11,6 @@ import (
) )
type IAdminV1 interface { type IAdminV1 interface {
AdminInfo(ctx context.Context, req *v1.AdminInfoReq) (res *v1.AdminInfoRes, err error) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error)
AdminEditPass(ctx context.Context, req *v1.AdminEditPassReq) (res *v1.AdminEditPassRes, err error) EditPass(ctx context.Context, req *v1.EditPassReq) (res *v1.EditPassRes, err error)
} }

View File

@ -4,21 +4,21 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type AdminInfoReq struct { type InfoReq struct {
g.Meta `path:"/admin/info" tags:"Admin" method:"get" summary:"管理员信息"` g.Meta `path:"/admin/info" tags:"Backend/Admin" method:"get" summary:"管理员信息"`
} }
type AdminInfoRes struct { type InfoRes struct {
g.Meta `mime:"application/json"` g.Meta `mime:"application/json"`
AdminId int64 `json:"adminId"` AdminId int64 `json:"adminId"`
Username string `json:"username"` Username string `json:"username"`
} }
type AdminEditPassReq struct { type EditPassReq struct {
g.Meta `path:"/admin/editPass" tags:"Admin" method:"post" summary:"修改密码"` g.Meta `path:"/admin/editPass" tags:"Backend/Admin" method:"post" summary:"修改密码"`
OldPass string `json:"oldPass" v:"required" dc:"旧密码"` OldPass string `json:"oldPass" v:"required" dc:"旧密码"`
NewPass string `json:"newPass" v:"required" dc:"新密码"` NewPass string `json:"newPass" v:"required" dc:"新密码"`
} }
type AdminEditPassRes struct { type EditPassRes struct {
g.Meta `mime:"application/json"` g.Meta `mime:"application/json"`
Success bool Success bool
} }

21
api/author/author.go Normal file
View File

@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package author
import (
"context"
"server/api/author/v1"
)
type IAuthorV1 interface {
List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error)
Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error)
Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error)
Follow(ctx context.Context, req *v1.FollowReq) (res *v1.FollowRes, err error)
Unfollow(ctx context.Context, req *v1.UnfollowReq) (res *v1.UnfollowRes, err error)
Detail(ctx context.Context, req *v1.DetailReq) (res *v1.DetailRes, err error)
}

75
api/author/v1/author.go Normal file
View File

@ -0,0 +1,75 @@
package v1
import (
"github.com/gogf/gf/v2/frame/g"
"server/internal/model"
)
type ListReq struct {
g.Meta `path:"/author" tags:"Backend/Admin" method:"get" summary:"获取作者列表"`
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
PenName string `json:"penName" dc:"笔名(模糊搜索)"`
Status int `json:"status" dc:"状态1正常2禁用"`
}
type ListRes struct {
Total int `json:"total" dc:"总数"`
List interface{} `json:"list" dc:"作者列表"`
}
type AddReq struct {
g.Meta `path:"/author" tags:"Backend/Admin" method:"post" summary:"新增作者"`
UserId int64 `json:"userId" dc:"用户ID" v:"required"`
PenName string `json:"penName" dc:"笔名" v:"required"`
Bio string `json:"bio" dc:"作者简介"`
Status int `json:"status" dc:"状态1正常2禁用"`
}
type AddRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type EditReq struct {
g.Meta `path:"/author" tags:"Backend/Admin" method:"put" summary:"编辑作者"`
Id int64 `json:"id" dc:"作者ID" v:"required"`
PenName string `json:"penName" dc:"笔名" v:"required"`
Bio string `json:"bio" dc:"作者简介"`
Status int `json:"status" dc:"状态1正常2禁用"`
}
type EditRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type DelReq struct {
g.Meta `path:"/author" tags:"Backend/Admin" method:"delete" summary:"删除作者"`
Id int64 `json:"id" dc:"作者ID" v:"required"`
}
type DelRes struct {
Success bool `json:"success" dc:"是否成功"`
}
// 关注作者
// =============================
type FollowReq struct {
g.Meta `path:"/author/follow" tags:"APP" method:"post" summary:"关注作者"`
AuthorId int64 `json:"authorId" dc:"作者ID" v:"required"`
}
type FollowRes struct {
Success bool `json:"success" dc:"是否成功"`
}
// 取消关注作者
// =============================
type UnfollowReq struct {
g.Meta `path:"/author/unfollow" tags:"APP" method:"post" summary:"取消关注作者"`
AuthorId int64 `json:"authorId" dc:"作者ID" v:"required"`
}
type UnfollowRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type DetailReq struct {
g.Meta `path:"/author/detail" tags:"APP" method:"get" summary:"作者详情"`
AuthorId int64 `json:"authorId" dc:"作者ID" v:"required"`
}
type DetailRes struct {
Author *model.AuthorDetailOut `json:"author" dc:"作者信息"`
}

View File

@ -11,8 +11,16 @@ import (
) )
type IBookV1 interface { type IBookV1 interface {
BookList(ctx context.Context, req *v1.BookListReq) (res *v1.BookListRes, err error) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
BookAdd(ctx context.Context, req *v1.BookAddReq) (res *v1.BookAddRes, err error) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error)
BookEdit(ctx context.Context, req *v1.BookEditReq) (res *v1.BookEditRes, err error) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error)
BookDel(ctx context.Context, req *v1.BookDelReq) (res *v1.BookDelRes, err error) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error)
ShelfAdd(ctx context.Context, req *v1.ShelfAddReq) (res *v1.ShelfAddRes, err error)
ShelfRemove(ctx context.Context, req *v1.ShelfRemoveReq) (res *v1.ShelfRemoveRes, err error)
AppList(ctx context.Context, req *v1.AppListReq) (res *v1.AppListRes, err error)
AppDetail(ctx context.Context, req *v1.AppDetailReq) (res *v1.AppDetailRes, err error)
AppRate(ctx context.Context, req *v1.AppRateReq) (res *v1.AppRateRes, err error)
MyList(ctx context.Context, req *v1.MyListReq) (res *v1.MyListRes, err error)
BookSetFeatured(ctx context.Context, req *v1.BookSetFeaturedReq) (res *v1.BookSetFeaturedRes, err error)
BookSetRecommended(ctx context.Context, req *v1.BookSetRecommendedReq) (res *v1.BookSetRecommendedRes, err error)
} }

View File

@ -6,56 +6,180 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type BookListReq struct { type ListReq struct {
g.Meta `path:"/book" tags:"Book" method:"get" summary:"获取小说列表"` g.Meta `path:"/book" tags:"Backend/Author" method:"get" summary:"获取小说列表"`
Page int `json:"page"` Page int `json:"page" dc:"页码"`
Size int `json:"size"` Size int `json:"size" dc:"每页数量"`
Title string `json:"title"` Title string `json:"title" dc:"书名模糊搜索"`
CategoryId int64 `json:"categoryId"` CategoryId int64 `json:"categoryId" dc:"分类ID"`
AuthorId int64 `json:"authorId"` AuthorId int64 `json:"authorId" dc:"作者ID"`
Status int `json:"status"` Status int `json:"status" dc:"状态"`
IsRecommended int `json:"isRecommended"` IsRecommended int `json:"isRecommended" dc:"是否推荐"`
Sort string `json:"sort" dc:"排序字段"`
} }
type BookListRes struct { type ListRes struct {
Total int `json:"total"` Total int `json:"total" dc:"总数"`
List []model.Book `json:"list"` List []model.Book `json:"list" dc:"书籍列表"`
} }
type BookAddReq struct { type AddReq struct {
g.Meta `path:"/book" tags:"Book" method:"post" summary:"新增小说"` g.Meta `path:"/book" tags:"Backend/Author" method:"post" summary:"新增小说"`
AuthorId int64 `json:"authorId"` AuthorId int64 `json:"authorId" dc:"作者ID" v:"required"`
CategoryId int64 `json:"categoryId"` CategoryId int64 `json:"categoryId" dc:"分类ID" v:"required"`
Title string `json:"title"` Title string `json:"title" dc:"书名" v:"required"`
CoverUrl string `json:"coverUrl"` CoverUrl string `json:"coverUrl" dc:"封面图"`
Description string `json:"description"` Description string `json:"description" dc:"简介"`
Status int `json:"status"` Status int `json:"status" dc:"状态"`
Tags string `json:"tags"` Tags string `json:"tags" dc:"标签"`
IsRecommended int `json:"isRecommended"` IsRecommended int `json:"isRecommended" dc:"是否推荐"`
IsFeatured int `json:"isFeatured" dc:"是否精选"`
Language string `json:"language" dc:"语言"`
} }
type BookAddRes struct { type AddRes struct {
Success bool `json:"success"` Success bool `json:"success" dc:"是否成功"`
} }
type BookEditReq struct { type EditReq struct {
g.Meta `path:"/book" tags:"Book" method:"put" summary:"编辑小说"` g.Meta `path:"/book" tags:"Backend/Author" method:"put" summary:"编辑小说"`
Id int64 `json:"id"` Id int64 `json:"id" dc:"书籍ID" v:"required"`
AuthorId int64 `json:"authorId"` AuthorId int64 `json:"authorId" dc:"作者ID" v:"required"`
CategoryId int64 `json:"categoryId"` CategoryId int64 `json:"categoryId" dc:"分类ID" v:"required"`
Title string `json:"title"` Title string `json:"title" dc:"书名" v:"required"`
CoverUrl string `json:"coverUrl"` CoverUrl string `json:"coverUrl" dc:"封面图"`
Description string `json:"description"` Description string `json:"description" dc:"简介"`
Status int `json:"status"` Status int `json:"status" dc:"状态"`
Tags string `json:"tags"` Tags string `json:"tags" dc:"标签"`
IsRecommended int `json:"isRecommended"` IsRecommended int `json:"isRecommended" dc:"是否推荐"`
IsFeatured int `json:"isFeatured" dc:"是否精选"`
Language string `json:"language" dc:"语言"`
} }
type BookEditRes struct { type EditRes struct {
Success bool `json:"success"` Success bool `json:"success" dc:"是否成功"`
} }
type BookDelReq struct { type DelReq struct {
g.Meta `path:"/book" tags:"Book" method:"delete" summary:"删除小说"` g.Meta `path:"/book" tags:"Backend/Author" method:"delete" summary:"删除小说"`
Id int64 `json:"id"` Id int64 `json:"id" dc:"书籍ID" v:"required"`
} }
type BookDelRes struct { type DelRes struct {
Success bool `json:"success"` Success bool `json:"success" dc:"是否成功"`
}
// 加入书架
type ShelfAddReq struct {
g.Meta `path:"/book/shelf/add" tags:"APP" method:"post" summary:"加入书架"`
BookId int64 `json:"bookId" dc:"小说ID" v:"required"`
}
type ShelfAddRes struct {
Success bool `json:"success" dc:"是否成功"`
}
// 移除书架
type ShelfRemoveReq struct {
g.Meta `path:"/book/shelf/remove" tags:"APP" method:"post" summary:"移除书架(支持批量)"`
BookIds []int64 `json:"bookIds" dc:"小说ID列表" v:"required"`
}
type ShelfRemoveRes struct {
Success bool `json:"success" dc:"是否成功"`
}
// App 获取书籍列表
type AppListReq struct {
g.Meta `path:"/book/app/list" tags:"APP" method:"get" summary:"App获取书籍列表"`
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
IsRecommended bool `json:"isRecommended" dc:"是否推荐"`
IsFeatured bool `json:"isFeatured" dc:"是否精选"`
IsLatest int `json:"isLatest" dc:"是否最新"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
Title string `json:"title" dc:"书名模糊搜索"`
AuthorId int `json:"authorId" dc:"作者ID"`
Language string `json:"language" dc:"语言"`
Sort string `json:"sort" dc:"排序字段"`
}
type AppListRes struct {
Total int `json:"total" dc:"总数"`
List []model.BookAppItem `json:"list" dc:"书籍列表"`
}
// App 获取书籍详情
type AppDetailReq struct {
g.Meta `path:"/book/app/detail" tags:"APP" method:"get" summary:"App获取书籍详情"`
Id int64 `json:"id" dc:"书籍ID" v:"required"`
}
type AppDetailRes struct {
Id int64 `json:"id" dc:"书籍ID"`
AuthorId int64 `json:"authorId" dc:"作者ID"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
Title string `json:"title" dc:"书名"`
CoverUrl string `json:"coverUrl" dc:"封面图"`
Description string `json:"description" dc:"简介"`
Status int `json:"status" dc:"状态"`
Tags string `json:"tags" dc:"标签"`
IsRecommended int `json:"isRecommended" dc:"是否推荐"`
IsFeatured int `json:"isFeatured" dc:"是否精选"`
Language string `json:"language" dc:"语言"`
Rating float64 `json:"rating" dc:"评分"`
CurrentReaders int64 `json:"currentReaders" dc:"在读人数"`
CreatedAt string `json:"createdAt" dc:"创建时间"`
UpdatedAt string `json:"updatedAt" dc:"更新时间"`
HasRead bool `json:"hasRead" dc:"是否读过"`
ReadProgress int `json:"readProgress" dc:"阅读进度百分比"`
LastChapterId int64 `json:"lastChapterId" dc:"最近阅读章节ID"`
LastReadAt string `json:"lastReadAt" dc:"最近阅读时间"`
}
// App 用户评分
type AppRateReq struct {
g.Meta `path:"/book/app/rate" tags:"APP" method:"post" summary:"App用户评分"`
BookId int64 `json:"bookId" dc:"书籍ID" v:"required"`
Rating float64 `json:"rating" dc:"评分(1-10分)" v:"required"`
}
type AppRateRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type BookAppItem struct {
Id int64 `json:"id" dc:"书籍ID"`
CoverUrl string `json:"coverUrl" dc:"封面图"`
Rating float64 `json:"rating" dc:"评分"`
Title string `json:"title" dc:"标题"`
Description string `json:"description" dc:"简介"`
HasRated bool `json:"hasRated" dc:"当前用户是否已评分"`
MyRating float64 `json:"myRating" dc:"当前用户评分未评分为0"`
}
// 我的书籍列表
// =============================
type MyListReq struct {
g.Meta `path:"/book/app/my-books" tags:"APP" method:"get" summary:"获取我的书籍列表"`
Type int `json:"type" dc:"类型1-正在读 2-已读完 3-历史记录" v:"required"`
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
Sort string `json:"sort" dc:"排序字段"`
}
type MyListRes struct {
Total int `json:"total" dc:"总数"`
List []model.MyBookItem `json:"list" dc:"书籍列表"`
}
// =============================
// 新增:单独修改精选状态和推荐状态
// =============================
type BookSetFeaturedReq struct {
g.Meta `path:"/book/set-featured" tags:"Backend/Book" method:"post" summary:"设置书籍精选状态"`
Id int64 `json:"id" dc:"书籍ID" v:"required"`
IsFeatured int `json:"isFeatured" dc:"是否精选" v:"required"`
}
type BookSetFeaturedRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type BookSetRecommendedReq struct {
g.Meta `path:"/book/set-recommended" tags:"Backend/Book" method:"post" summary:"设置书籍推荐状态"`
Id int64 `json:"id" dc:"书籍ID" v:"required"`
IsRecommended int `json:"isRecommended" dc:"是否推荐" v:"required"`
}
type BookSetRecommendedRes struct {
Success bool `json:"success" dc:"是否成功"`
} }

View File

@ -11,8 +11,8 @@ import (
) )
type ICategoryV1 interface { type ICategoryV1 interface {
CategoryList(ctx context.Context, req *v1.CategoryListReq) (res *v1.CategoryListRes, err error) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
CategoryAdd(ctx context.Context, req *v1.CategoryAddReq) (res *v1.CategoryAddRes, err error) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error)
CategoryEdit(ctx context.Context, req *v1.CategoryEditReq) (res *v1.CategoryEditRes, err error) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error)
CategoryDel(ctx context.Context, req *v1.CategoryDelReq) (res *v1.CategoryDelRes, err error) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error)
} }

View File

@ -6,41 +6,41 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type CategoryListReq struct { type ListReq struct {
g.Meta `path:"/category" tags:"Category" method:"get" summary:"获取分类列表"` g.Meta `path:"/category" tags:"APP" method:"get" summary:"获取分类列表"`
Page int `json:"page" dc:"页码"` Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"` Size int `json:"size" dc:"每页数量"`
Name string `json:"name" dc:"分类名称(模糊搜索)"` Name string `json:"name" dc:"分类名称(模糊搜索)"`
Type int `json:"type" dc:"类型1男频2女频"` Channel int `json:"channel" dc:"频道类型1=男频2=女频"`
} }
type CategoryListRes struct { type ListRes struct {
Total int `json:"total" dc:"总数"` Total int `json:"total" dc:"总数"`
List []model.Category `json:"list" dc:"分类列表"` List []model.Category `json:"list" dc:"分类列表"`
} }
type CategoryAddReq struct { type AddReq struct {
g.Meta `path:"/category" tags:"Category" method:"post" summary:"新增分类"` g.Meta `path:"/category" tags:"Backend/Admin" method:"post" summary:"新增分类"`
Name string `json:"name" dc:"分类名称"` Name string `json:"name" dc:"分类名称" v:"required"`
Type int `json:"type" dc:"类型1男频2女频"` Channel int `json:"channel" dc:"频道类型1=男频2=女频" v:"required"`
} }
type CategoryAddRes struct { type AddRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }
type CategoryEditReq struct { type EditReq struct {
g.Meta `path:"/category" tags:"Category" method:"put" summary:"编辑分类"` g.Meta `path:"/category" tags:"Backend/Admin" method:"put" summary:"编辑分类"`
Id int `json:"id" dc:"分类ID"` Id int64 `json:"id" dc:"分类ID" v:"required"`
Name string `json:"name" dc:"分类名称"` Name string `json:"name" dc:"分类名称" v:"required"`
Type int `json:"type" dc:"类型1男频2女频"` Channel int `json:"channel" dc:"频道类型1=男频2=女频" v:"required"`
} }
type CategoryEditRes struct { type EditRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }
type CategoryDelReq struct { type DelReq struct {
g.Meta `path:"/category" tags:"Category" method:"delete" summary:"删除分类"` g.Meta `path:"/category" tags:"Backend/Admin" method:"delete" summary:"删除分类"`
Id int `json:"id" dc:"分类ID"` Id int64 `json:"id" dc:"分类ID" v:"required"`
} }
type CategoryDelRes struct { type DelRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }

View File

@ -11,8 +11,12 @@ import (
) )
type IChapterV1 interface { type IChapterV1 interface {
ChapterList(ctx context.Context, req *v1.ChapterListReq) (res *v1.ChapterListRes, err error) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
ChapterAdd(ctx context.Context, req *v1.ChapterAddReq) (res *v1.ChapterAddRes, err error) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error)
ChapterEdit(ctx context.Context, req *v1.ChapterEditReq) (res *v1.ChapterEditRes, err error) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error)
ChapterDel(ctx context.Context, req *v1.ChapterDelReq) (res *v1.ChapterDelRes, err error) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error)
AppList(ctx context.Context, req *v1.AppListReq) (res *v1.AppListRes, err error)
AppDetail(ctx context.Context, req *v1.AppDetailReq) (res *v1.AppDetailRes, err error)
AppPurchase(ctx context.Context, req *v1.AppPurchaseReq) (res *v1.AppPurchaseRes, err error)
AppProgress(ctx context.Context, req *v1.AppProgressReq) (res *v1.AppProgressRes, err error)
} }

View File

@ -6,52 +6,98 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type ChapterListReq struct { type ListReq struct {
g.Meta `path:"/chapter" tags:"Chapter" method:"get" summary:"获取章节列表"` g.Meta `path:"/chapter" tags:"Backend/Author" method:"get" summary:"获取章节列表"`
Page int `json:"page" dc:"页码"` Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"` Size int `json:"size" dc:"每页数量"`
BookId int64 `json:"bookId" dc:"小说ID"` BookId int64 `json:"bookId" dc:"小说ID"`
Title string `json:"title" dc:"章节标题(模糊搜索)"` Title string `json:"title" dc:"章节标题(模糊搜索)"`
IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"` IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"`
} }
type ChapterListRes struct { type ListRes struct {
Total int `json:"total" dc:"总数"` Total int `json:"total" dc:"总数"`
List []model.Chapter `json:"list" dc:"章节列表"` List []model.Chapter `json:"list" dc:"章节列表"`
} }
type ChapterAddReq struct { type AddReq struct {
g.Meta `path:"/chapter" tags:"Chapter" method:"post" summary:"新增章节"` g.Meta `path:"/chapter" tags:"Backend/Author" method:"post" summary:"新增章节"`
BookId int64 `json:"bookId" dc:"小说ID"` BookId int64 `json:"bookId" dc:"小说ID" v:"required"`
Title string `json:"title" dc:"章节标题"` Title string `json:"title" dc:"章节标题" v:"required"`
Content string `json:"content" dc:"章节内容"` Content string `json:"content" dc:"章节内容" v:"required"`
WordCount int `json:"wordCount" dc:"章节字数"` WordCount int `json:"wordCount" dc:"章节字数"`
Sort int `json:"sort" dc:"排序序号"` Sort int `json:"sort" dc:"排序序号"`
IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"` IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"`
RequiredScore int `json:"requiredScore" dc:"解锁所需积分"` RequiredScore int `json:"requiredScore" dc:"解锁所需积分"`
} }
type ChapterAddRes struct { type AddRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }
type ChapterEditReq struct { type EditReq struct {
g.Meta `path:"/chapter" tags:"Chapter" method:"put" summary:"编辑章节"` g.Meta `path:"/chapter" tags:"Backend/Author" method:"put" summary:"编辑章节"`
Id int64 `json:"id" dc:"章节ID"` Id int64 `json:"id" dc:"章节ID" v:"required"`
BookId int64 `json:"bookId" dc:"小说ID"` BookId int64 `json:"bookId" dc:"小说ID" v:"required"`
Title string `json:"title" dc:"章节标题"` Title string `json:"title" dc:"章节标题" v:"required"`
Content string `json:"content" dc:"章节内容"` Content string `json:"content" dc:"章节内容" v:"required"`
WordCount int `json:"wordCount" dc:"章节字数"` WordCount int `json:"wordCount" dc:"章节字数"`
Sort int `json:"sort" dc:"排序序号"` Sort int `json:"sort" dc:"排序序号"`
IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"` IsLocked int `json:"isLocked" dc:"是否锁定0免费1需积分解锁"`
RequiredScore int `json:"requiredScore" dc:"解锁所需积分"` RequiredScore int `json:"requiredScore" dc:"解锁所需积分"`
} }
type ChapterEditRes struct { type EditRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }
type ChapterDelReq struct { type DelReq struct {
g.Meta `path:"/chapter" tags:"Chapter" method:"delete" summary:"删除章节"` g.Meta `path:"/chapter" tags:"Backend/Author" method:"delete" summary:"删除章节"`
Id int64 `json:"id" dc:"章节ID"` Id int64 `json:"id" dc:"章节ID" v:"required"`
} }
type ChapterDelRes struct { type DelRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type AppListReq struct {
g.Meta `path:"/chapter/app/list" tags:"APP" method:"get" summary:"App获取章节列表"`
BookId int64 `json:"bookId" dc:"书籍ID" v:"required"`
IsDesc bool `json:"isDesc" dc:"是否逆序排列"`
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
}
type AppListRes struct {
Total int `json:"total" dc:"总数"`
List []model.ChapterAppItem `json:"list" dc:"章节列表"`
}
type AppDetailReq struct {
g.Meta `path:"/chapter/app/detail" tags:"APP" method:"get" summary:"App获取章节详情"`
Id int64 `json:"id" dc:"章节ID" v:"required"`
}
type AppDetailRes struct {
Id int64 `json:"id" dc:"章节ID"`
BookId int64 `json:"bookId" dc:"书籍ID"`
Title string `json:"title" dc:"章节标题"`
Content string `json:"content" dc:"章节内容"`
WordCount int `json:"wordCount" dc:"字数"`
Sort int `json:"sort" dc:"排序"`
IsLocked int `json:"isLocked" dc:"是否锁定"`
RequiredScore int `json:"requiredScore" dc:"所需积分"`
UpdatedAt string `json:"updatedAt" dc:"更新时间"`
}
type AppPurchaseReq struct {
g.Meta `path:"/chapter/app/purchase" tags:"APP" method:"post" summary:"App购买章节"`
Id int64 `json:"id" dc:"章节ID" v:"required"`
}
type AppPurchaseRes struct {
Success bool `json:"success" dc:"是否成功"`
}
type AppProgressReq struct {
g.Meta `path:"/chapter/app/progress" tags:"APP" method:"post" summary:"App上传阅读进度"`
BookId int64 `json:"bookId" dc:"书籍ID" v:"required"`
ChapterId int64 `json:"chapterId" dc:"章节ID" v:"required"`
Progress int `json:"progress" dc:"阅读进度百分比(0-100)"`
}
type AppProgressRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }

View File

@ -11,6 +11,6 @@ import (
) )
type IFeedbackV1 interface { type IFeedbackV1 interface {
FeedbackList(ctx context.Context, req *v1.FeedbackListReq) (res *v1.FeedbackListRes, err error) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
FeedbackAdd(ctx context.Context, req *v1.FeedbackAddReq) (res *v1.FeedbackAddRes, err error) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error)
} }

View File

@ -6,22 +6,22 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type FeedbackListReq struct { type ListReq struct {
g.Meta `path:"/feedback" tags:"Feedback" method:"get" summary:"获取反馈列表"` g.Meta `path:"/feedback" tags:"Backend/Admin" method:"get" summary:"获取反馈列表"`
Page int `json:"page" dc:"页码"` Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"` Size int `json:"size" dc:"每页数量"`
UserId int64 `json:"userId" dc:"用户ID"` UserId int64 `json:"userId" dc:"用户ID"`
Status int `json:"status" dc:"处理状态1未处理2处理中3已处理"` Status int `json:"status" dc:"处理状态1未处理2处理中3已处理"`
} }
type FeedbackListRes struct { type ListRes struct {
Total int `json:"total" dc:"总数"` Total int `json:"total" dc:"总数"`
List []model.Feedback `json:"list" dc:"反馈列表"` List []model.Feedback `json:"list" dc:"反馈列表"`
} }
type FeedbackAddReq struct { type AddReq struct {
g.Meta `path:"/feedback" tags:"APP/Feedback" method:"post" summary:"新增反馈"` g.Meta `path:"/feedback" tags:"APP" method:"post" summary:"新增反馈"`
Content string `json:"content" dc:"反馈内容"` Content string `json:"content" dc:"反馈内容" v:"required"`
} }
type FeedbackAddRes struct { type AddRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`
} }

View File

@ -11,7 +11,7 @@ import (
) )
type IUserV1 interface { type IUserV1 interface {
UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error)
Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error) Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error)
Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error)
} }

View File

@ -4,10 +4,10 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
type UserInfoReq struct { type InfoReq struct {
g.Meta `path:"/user/info" tags:"APP/User" method:"get" summary:"获取用户信息"` g.Meta `path:"/user/info" tags:"APP" method:"get" summary:"获取用户信息"`
} }
type UserInfoRes struct { type InfoRes struct {
g.Meta `mime:"application/json"` g.Meta `mime:"application/json"`
UserId int64 `json:"userId"` UserId int64 `json:"userId"`
Username string `json:"username"` // 用户名 Username string `json:"username"` // 用户名
@ -17,7 +17,7 @@ type UserInfoRes struct {
} }
type DeleteReq struct { type DeleteReq struct {
g.Meta `path:"/user/delete" tags:"APP/User" method:"post" summary:"删除用户"` g.Meta `path:"/user/delete" tags:"APP" method:"post" summary:"删除用户"`
Password string `json:"password" v:"required" dc:"密码"` Password string `json:"password" v:"required" dc:"密码"`
} }
@ -26,7 +26,7 @@ type DeleteRes struct {
} }
type LogoutReq struct { type LogoutReq struct {
g.Meta `path:"/user/logout" tags:"APP/User" method:"post" summary:"登出"` g.Meta `path:"/user/logout" tags:"APP" method:"post" summary:"登出"`
} }
type LogoutRes struct { type LogoutRes struct {
Success bool `json:"success" dc:"是否成功"` Success bool `json:"success" dc:"是否成功"`

1
build_x86_64.sh Executable file
View File

@ -0,0 +1 @@
GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o ./noveltest ./main.go

18
go.mod
View File

@ -5,7 +5,10 @@ go 1.23.0
toolchain go1.24.3 toolchain go1.24.3
require ( require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/aws/aws-sdk-go v1.55.7
github.com/aws/aws-sdk-go-v2 v1.36.5
github.com/aws/aws-sdk-go-v2/config v1.29.17
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8
github.com/casbin/casbin/v2 v2.108.0 github.com/casbin/casbin/v2 v2.108.0
github.com/gogf/gf v1.16.9 github.com/gogf/gf v1.16.9
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0 github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0
@ -19,6 +22,17 @@ require (
require ( require (
github.com/BurntSushi/toml v1.4.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
github.com/aws/smithy-go v1.22.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/casbin/govaluate v1.3.0 // indirect github.com/casbin/govaluate v1.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
@ -34,6 +48,7 @@ require (
github.com/gomodule/redigo v1.8.5 // indirect github.com/gomodule/redigo v1.8.5 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/magiconair/properties v1.8.9 // indirect github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
@ -48,6 +63,5 @@ require (
golang.org/x/net v0.32.0 // indirect golang.org/x/net v0.32.0 // indirect
golang.org/x/sys v0.28.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.12.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

40
go.sum
View File

@ -1,8 +1,36 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0=
github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0=
github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0=
github.com/aws/aws-sdk-go-v2/config v1.29.17/go.mod h1:9P4wwACpbeXs9Pm9w1QTh6BwWwJjwYvJ1iCt5QbCXh8=
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 h1:ONnH5CM16RTXRkS8Z1qg7/s2eDOhHhaXVd72mmyv4/0=
github.com/aws/aws-sdk-go-v2/credentials v1.17.70/go.mod h1:M+lWhhmomVGgtuPOhO85u4pEa3SmssPTdcYpP/5J/xc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 h1:KAXP9JSHO1vKGCr5f4O6WmlVKLFFXgWYAGoJosorxzU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32/go.mod h1:h4Sg6FQdexC1yYG9RDnOvLbW1a/P986++/Y/a+GyEM8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 h1:SsytQyTMHMDPspp+spo7XwXTP44aJZZAC7fBV2C5+5s=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36/go.mod h1:Q1lnJArKRXkenyog6+Y+zr7WDpk4e6XlR6gs20bbeNo=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 h1:i2vNHQiXUvKhs3quBR6aqlgJaiaexz/aNvdCktW/kAM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36/go.mod h1:UdyGa7Q91id/sdyHPwth+043HhmP6yP9MBHgbZM0xo8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8 h1:80dpSqWMwx2dAm30Ib7J6ucz1ZHfiv5OCRwN/EnCOXQ=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8/go.mod h1:IzNt/udsXlETCdvBOL0nmyMe2t9cGmXmZgsdoZGYYhI=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 h1:BpOxT3yhLwSJ77qIY3DoHAQjZsc4HEGfMCE4NGy3uFg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3/go.mod h1:vq/GQR1gOFLquZMSrxUK/cpvKCNVYibNyJ1m7JrU88E=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1ezajK+yQmkwnPV0=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w=
github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw=
github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@ -67,6 +95,10 @@ github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtg
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
github.com/hailaz/gf-casbin-adapter/v2 v2.8.1 h1:ZFIlfQAYmrL2Fe6/dZz6vCA5hYK0NxhnoKMQpbklgqc= github.com/hailaz/gf-casbin-adapter/v2 v2.8.1 h1:ZFIlfQAYmrL2Fe6/dZz6vCA5hYK0NxhnoKMQpbklgqc=
github.com/hailaz/gf-casbin-adapter/v2 v2.8.1/go.mod h1:Jk91dRBZuMVjBMu2oQmqMRc6xuL5V+rzgHeFWvI+wlg= github.com/hailaz/gf-casbin-adapter/v2 v2.8.1/go.mod h1:Jk91dRBZuMVjBMu2oQmqMRc6xuL5V+rzgHeFWvI+wlg=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -132,8 +164,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -141,6 +171,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"server/internal/controller/admin" "server/internal/controller/admin"
"server/internal/controller/auth" "server/internal/controller/auth"
"server/internal/controller/author"
"server/internal/controller/book" "server/internal/controller/book"
"server/internal/controller/category" "server/internal/controller/category"
"server/internal/controller/chapter" "server/internal/controller/chapter"
@ -35,6 +36,7 @@ var (
group.Middleware(middleware.Casbin) group.Middleware(middleware.Casbin)
group.Bind( group.Bind(
admin.NewV1(), admin.NewV1(),
author.NewV1(),
book.NewV1(), book.NewV1(),
category.NewV1(), category.NewV1(),
chapter.NewV1(), chapter.NewV1(),

View File

@ -1,17 +0,0 @@
package admin
import (
"context"
"server/internal/model"
"server/internal/service"
"server/api/admin/v1"
)
func (c *ControllerV1) AdminEditPass(ctx context.Context, req *v1.AdminEditPassReq) (res *v1.AdminEditPassRes, err error) {
out, err := service.Admin().EditPass(ctx, &model.AdminEditPassIn{NewPass: req.NewPass, OldPass: req.OldPass})
if err != nil {
return nil, err
}
return &v1.AdminEditPassRes{Success: out.Success}, nil
}

View File

@ -0,0 +1,14 @@
package admin
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"server/api/admin/v1"
)
func (c *ControllerV1) EditPass(ctx context.Context, req *v1.EditPassReq) (res *v1.EditPassRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

View File

@ -9,13 +9,14 @@ import (
"server/api/admin/v1" "server/api/admin/v1"
) )
func (c *ControllerV1) AdminInfo(ctx context.Context, req *v1.AdminInfoReq) (res *v1.AdminInfoRes, err error) { func (c *ControllerV1) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error) {
adminId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64() adminId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Admin().Info(ctx, &model.AdminInfoIn{AdminId: adminId}) out, err := service.Admin().Info(ctx, &model.AdminInfoIn{AdminId: adminId})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.AdminInfoRes{ return &v1.InfoRes{
AdminId: out.AdminId, AdminId: out.AdminId,
Username: out.Username, Username: out.Username,
}, nil }, nil

View File

@ -0,0 +1,5 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package author

View File

@ -0,0 +1,15 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package author
import (
"server/api/author"
)
type ControllerV1 struct{}
func NewV1() author.IAuthorV1 {
return &ControllerV1{}
}

View File

@ -0,0 +1,24 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error) {
out, err := service.Author().Create(ctx, &model.AuthorAddIn{
UserId: req.UserId,
PenName: req.PenName,
Bio: req.Bio,
Status: req.Status,
})
if err != nil {
return nil, err
}
return &v1.AddRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,21 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error) {
out, err := service.Author().Delete(ctx, &model.AuthorDelIn{
Id: req.Id,
})
if err != nil {
return nil, err
}
return &v1.DelRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,17 @@
package author
import (
"context"
"server/internal/model"
"server/internal/service"
"server/api/author/v1"
)
func (c *ControllerV1) Detail(ctx context.Context, req *v1.DetailReq) (res *v1.DetailRes, err error) {
out, err := service.Author().Detail(ctx, &model.AuthorDetailIn{AuthorId: req.AuthorId})
if err != nil {
return nil, err
}
return &v1.DetailRes{Author: out}, nil
}

View File

@ -0,0 +1,24 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error) {
out, err := service.Author().Update(ctx, &model.AuthorEditIn{
Id: req.Id,
PenName: req.PenName,
Bio: req.Bio,
Status: req.Status,
})
if err != nil {
return nil, err
}
return &v1.EditRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,31 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/model"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) Follow(ctx context.Context, req *v1.FollowReq) (res *v1.FollowRes, err error) {
// 从 ctx 获取用户ID
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
if userId == 0 {
return nil, ecode.Logout
}
out, err := service.UserFollowAuthor().Create(ctx, &model.UserFollowAuthorAddIn{
UserId: userId,
AuthorId: req.AuthorId,
})
if err != nil {
return nil, err
}
return &v1.FollowRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,25 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
out, err := service.Author().List(ctx, &model.AuthorListIn{
Page: req.Page,
Size: req.Size,
PenName: req.PenName,
Status: req.Status,
})
if err != nil {
return nil, err
}
return &v1.ListRes{
Total: out.Total,
List: out.List,
}, nil
}

View File

@ -0,0 +1,27 @@
package author
import (
"context"
v1 "server/api/author/v1"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) Unfollow(ctx context.Context, req *v1.UnfollowReq) (res *v1.UnfollowRes, err error) {
// 从 ctx 获取用户ID
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
if userId == 0 {
return nil, ecode.Logout
}
out, err := service.UserFollowAuthor().Unfollow(ctx, userId, req.AuthorId)
if err != nil {
return nil, err
}
return &v1.UnfollowRes{
Success: out.Success,
}, nil
}

View File

@ -2,25 +2,27 @@ package book
import ( import (
"context" "context"
v1 "server/api/book/v1"
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/book/v1"
) )
func (c *ControllerV1) BookAdd(ctx context.Context, req *v1.BookAddReq) (res *v1.BookAddRes, err error) { func (c *ControllerV1) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error) {
out, err := service.Book().Create(ctx, &model.BookAddIn{ out, err := service.Book().Create(ctx, &model.BookAddIn{
AuthorId: req.AuthorId, AuthorId: req.AuthorId,
CategoryId: req.CategoryId, CategoryId: req.CategoryId,
Title: req.Title,
CoverUrl: req.CoverUrl, CoverUrl: req.CoverUrl,
Description: req.Description, Description: req.Description,
IsRecommended: req.IsRecommended,
Status: req.Status, Status: req.Status,
Tags: req.Tags, Tags: req.Tags,
Title: req.Title, IsRecommended: req.IsRecommended,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.BookAddRes{Success: out.Success}, nil
return &v1.AddRes{
Success: out.Success,
}, nil
} }

View File

@ -0,0 +1,44 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppDetail(ctx context.Context, req *v1.AppDetailReq) (res *v1.AppDetailRes, err error) {
// 从 ctx 获取用户ID
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Book().AppDetail(ctx, &model.BookAppDetailIn{
Id: req.Id,
UserId: userId,
})
if err != nil {
return nil, err
}
return &v1.AppDetailRes{
Id: out.Id,
AuthorId: out.AuthorId,
CategoryId: out.CategoryId,
Title: out.Title,
CoverUrl: out.CoverUrl,
Description: out.Description,
Status: out.Status,
Tags: out.Tags,
IsRecommended: out.IsRecommended,
Rating: out.Rating,
CurrentReaders: out.CurrentReaders,
CreatedAt: out.CreatedAt.String(),
UpdatedAt: out.UpdatedAt.String(),
// 添加阅读进度信息
HasRead: out.HasRead,
ReadProgress: out.ReadProgress,
LastChapterId: out.LastChapterId,
LastReadAt: out.LastReadAt,
}, nil
}

View File

@ -0,0 +1,35 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppList(ctx context.Context, req *v1.AppListReq) (res *v1.AppListRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Book().AppList(ctx, &model.BookAppListIn{
Page: req.Page,
Size: req.Size,
IsRecommended: req.IsRecommended,
IsFeatured: req.IsFeatured,
IsLatest: req.IsLatest,
CategoryId: req.CategoryId,
Title: req.Title,
AuthorId: req.AuthorId,
UserId: userId,
Language: req.Language,
Sort: req.Sort,
})
if err != nil {
return nil, err
}
return &v1.AppListRes{
Total: out.Total,
List: out.List,
}, nil
}

View File

@ -0,0 +1,26 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppRate(ctx context.Context, req *v1.AppRateReq) (res *v1.AppRateRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Book().AppRate(ctx, &model.BookAppRateIn{
BookId: req.BookId,
Rating: req.Rating,
UserId: userId,
})
if err != nil {
return nil, err
}
return &v1.AppRateRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,14 @@
package book
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"server/api/book/v1"
)
func (c *ControllerV1) BookSetFeatured(ctx context.Context, req *v1.BookSetFeaturedReq) (res *v1.BookSetFeaturedRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

View File

@ -0,0 +1,14 @@
package book
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"server/api/book/v1"
)
func (c *ControllerV1) BookSetRecommended(ctx context.Context, req *v1.BookSetRecommendedReq) (res *v1.BookSetRecommendedRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

View File

@ -2,18 +2,20 @@ package book
import ( import (
"context" "context"
v1 "server/api/book/v1"
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/book/v1"
) )
func (c *ControllerV1) BookDel(ctx context.Context, req *v1.BookDelReq) (res *v1.BookDelRes, err error) { func (c *ControllerV1) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error) {
out, err := service.Book().Delete(ctx, &model.BookDelIn{ out, err := service.Book().Delete(ctx, &model.BookDelIn{
Id: req.Id, Id: req.Id,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.BookDelRes{Success: out.Success}, nil
return &v1.DelRes{
Success: out.Success,
}, nil
} }

View File

@ -2,26 +2,28 @@ package book
import ( import (
"context" "context"
v1 "server/api/book/v1"
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/book/v1"
) )
func (c *ControllerV1) BookEdit(ctx context.Context, req *v1.BookEditReq) (res *v1.BookEditRes, err error) { func (c *ControllerV1) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error) {
out, err := service.Book().Update(ctx, &model.BookEditIn{ out, err := service.Book().Update(ctx, &model.BookEditIn{
Id: req.Id,
AuthorId: req.AuthorId, AuthorId: req.AuthorId,
CategoryId: req.CategoryId, CategoryId: req.CategoryId,
Title: req.Title,
CoverUrl: req.CoverUrl, CoverUrl: req.CoverUrl,
Description: req.Description, Description: req.Description,
Id: req.Id,
IsRecommended: req.IsRecommended,
Status: req.Status, Status: req.Status,
Tags: req.Tags, Tags: req.Tags,
Title: req.Title, IsRecommended: req.IsRecommended,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.BookEditRes{Success: out.Success}, nil
return &v1.EditRes{
Success: out.Success,
}, nil
} }

View File

@ -2,27 +2,28 @@ package book
import ( import (
"context" "context"
v1 "server/api/book/v1"
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/book/v1"
) )
func (c *ControllerV1) BookList(ctx context.Context, req *v1.BookListReq) (res *v1.BookListRes, err error) { func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
out, err := service.Book().List(ctx, &model.BookListIn{ out, err := service.Book().List(ctx, &model.BookListIn{
AuthorId: req.AuthorId,
CategoryId: req.CategoryId,
IsRecommended: req.IsRecommended,
Page: req.Page, Page: req.Page,
Size: req.Size, Size: req.Size,
Status: req.Status,
Title: req.Title, Title: req.Title,
CategoryId: req.CategoryId,
AuthorId: req.AuthorId,
Status: req.Status,
IsRecommended: req.IsRecommended,
Sort: req.Sort,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.BookListRes{
List: out.List, return &v1.ListRes{
Total: out.Total, Total: out.Total,
List: out.List,
}, nil }, nil
} }

View File

@ -0,0 +1,32 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/net/ghttp"
)
func (c *ControllerV1) MyList(ctx context.Context, req *v1.MyListReq) (res *v1.MyListRes, err error) {
// 从ctx获取userId
userId := ghttp.RequestFromCtx(ctx).GetCtxVar("id").Int64()
in := &model.MyBookListIn{
Type: req.Type,
Page: req.Page,
Size: req.Size,
UserId: userId,
Sort: req.Sort,
}
result, err := service.Book().MyList(ctx, in)
if err != nil {
return nil, err
}
res = &v1.MyListRes{
Total: result.Total,
List: result.List,
}
return
}

View File

@ -0,0 +1,31 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) ShelfAdd(ctx context.Context, req *v1.ShelfAddReq) (res *v1.ShelfAddRes, err error) {
// 从 ctx 获取用户ID
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
if userId == 0 {
return nil, ecode.Logout
}
out, err := service.Bookshelve().Add(ctx, &model.BookshelveAddIn{
UserId: userId,
BookId: req.BookId,
})
if err != nil {
return nil, err
}
return &v1.ShelfAddRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,31 @@
package book
import (
"context"
v1 "server/api/book/v1"
"server/internal/model"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) ShelfRemove(ctx context.Context, req *v1.ShelfRemoveReq) (res *v1.ShelfRemoveRes, err error) {
// 从 ctx 获取用户ID
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
if userId == 0 {
return nil, ecode.Logout
}
out, err := service.Bookshelve().Delete(ctx, &model.BookshelveDelIn{
UserId: userId,
BookIds: req.BookIds,
})
if err != nil {
return nil, err
}
return &v1.ShelfRemoveRes{
Success: out.Success,
}, nil
}

View File

@ -5,18 +5,18 @@ import (
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/category/v1" v1 "server/api/category/v1"
) )
func (c *ControllerV1) CategoryAdd(ctx context.Context, req *v1.CategoryAddReq) (res *v1.CategoryAddRes, err error) { func (c *ControllerV1) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error) {
out, err := service.Category().Create(ctx, &model.CategoryAddIn{ out, err := service.Category().Create(ctx, &model.CategoryAddIn{
Name: req.Name, Name: req.Name,
Type: req.Type, Channel: req.Channel,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.CategoryAddRes{ return &v1.AddRes{
Success: out.Success, Success: out.Success,
}, nil }, nil
} }

View File

@ -1,25 +0,0 @@
package category
import (
"context"
"server/internal/model"
"server/internal/service"
"server/api/category/v1"
)
func (c *ControllerV1) CategoryList(ctx context.Context, req *v1.CategoryListReq) (res *v1.CategoryListRes, err error) {
out, err := service.Category().List(ctx, &model.CategoryListIn{
Name: req.Name,
Page: req.Page,
Size: req.Size,
Type: req.Type,
})
if err != nil {
return nil, err
}
return &v1.CategoryListRes{
List: out.List,
Total: out.Total,
}, nil
}

View File

@ -8,14 +8,14 @@ import (
"server/api/category/v1" "server/api/category/v1"
) )
func (c *ControllerV1) CategoryDel(ctx context.Context, req *v1.CategoryDelReq) (res *v1.CategoryDelRes, err error) { func (c *ControllerV1) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error) {
out, err := service.Category().Delete(ctx, &model.CategoryDelIn{ out, err := service.Category().Delete(ctx, &model.CategoryDelIn{
Id: req.Id, Id: req.Id,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.CategoryDelRes{ return &v1.DelRes{
Success: out.Success, Success: out.Success,
}, nil }, nil
} }

View File

@ -5,19 +5,19 @@ import (
"server/internal/model" "server/internal/model"
"server/internal/service" "server/internal/service"
"server/api/category/v1" v1 "server/api/category/v1"
) )
func (c *ControllerV1) CategoryEdit(ctx context.Context, req *v1.CategoryEditReq) (res *v1.CategoryEditRes, err error) { func (c *ControllerV1) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error) {
out, err := service.Category().Update(ctx, &model.CategoryEditIn{ out, err := service.Category().Update(ctx, &model.CategoryEditIn{
Id: req.Id, Id: req.Id,
Name: req.Name, Name: req.Name,
Type: req.Type, Channel: req.Channel,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.CategoryEditRes{ return &v1.EditRes{
Success: out.Success, Success: out.Success,
}, nil }, nil
} }

View File

@ -0,0 +1,30 @@
package category
import (
"context"
v1 "server/api/category/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
// 调用service层获取分类列表
result, err := service.Category().List(ctx, &model.CategoryListIn{
Page: req.Page,
Size: req.Size,
Name: req.Name,
Channel: req.Channel,
})
if err != nil {
return nil, err
}
// 构造响应
res = &v1.ListRes{
Total: result.Total,
List: result.List,
}
return res, nil
}

View File

@ -8,7 +8,7 @@ import (
"server/api/chapter/v1" "server/api/chapter/v1"
) )
func (c *ControllerV1) ChapterAdd(ctx context.Context, req *v1.ChapterAddReq) (res *v1.ChapterAddRes, err error) { func (c *ControllerV1) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error) {
out, err := service.Chapter().Create(ctx, &model.ChapterAddIn{ out, err := service.Chapter().Create(ctx, &model.ChapterAddIn{
BookId: req.BookId, BookId: req.BookId,
Content: req.Content, Content: req.Content,
@ -21,7 +21,7 @@ func (c *ControllerV1) ChapterAdd(ctx context.Context, req *v1.ChapterAddReq) (r
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.ChapterAddRes{ return &v1.AddRes{
Success: out.Success, Success: out.Success,
}, nil }, nil
} }

View File

@ -0,0 +1,29 @@
package chapter
import (
"context"
v1 "server/api/chapter/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) AppDetail(ctx context.Context, req *v1.AppDetailReq) (res *v1.AppDetailRes, err error) {
out, err := service.Chapter().AppDetail(ctx, &model.ChapterAppDetailIn{
Id: req.Id,
})
if err != nil {
return nil, err
}
return &v1.AppDetailRes{
Id: out.Id,
BookId: out.BookId,
Title: out.Title,
Content: out.Content,
WordCount: out.WordCount,
Sort: out.Sort,
IsLocked: out.IsLocked,
RequiredScore: out.RequiredScore,
UpdatedAt: out.UpdatedAt.String(),
}, nil
}

View File

@ -0,0 +1,29 @@
package chapter
import (
"context"
v1 "server/api/chapter/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppList(ctx context.Context, req *v1.AppListReq) (res *v1.AppListRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Chapter().AppList(ctx, &model.ChapterAppListIn{
BookId: req.BookId,
IsDesc: req.IsDesc,
Page: req.Page,
Size: req.Size,
UserId: userId,
})
if err != nil {
return nil, err
}
return &v1.AppListRes{
Total: out.Total,
List: out.List,
}, nil
}

View File

@ -0,0 +1,27 @@
package chapter
import (
"context"
v1 "server/api/chapter/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppProgress(ctx context.Context, req *v1.AppProgressReq) (res *v1.AppProgressRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Chapter().AppProgress(ctx, &model.ChapterAppProgressIn{
BookId: req.BookId,
ChapterId: req.ChapterId,
Progress: req.Progress,
UserId: userId,
})
if err != nil {
return nil, err
}
return &v1.AppProgressRes{
Success: out.Success,
}, nil
}

View File

@ -0,0 +1,25 @@
package chapter
import (
"context"
v1 "server/api/chapter/v1"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) AppPurchase(ctx context.Context, req *v1.AppPurchaseReq) (res *v1.AppPurchaseRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Chapter().AppPurchase(ctx, &model.ChapterAppPurchaseIn{
Id: req.Id,
UserId: userId,
})
if err != nil {
return nil, err
}
return &v1.AppPurchaseRes{
Success: out.Success,
}, nil
}

View File

@ -8,12 +8,12 @@ import (
"server/api/chapter/v1" "server/api/chapter/v1"
) )
func (c *ControllerV1) ChapterDel(ctx context.Context, req *v1.ChapterDelReq) (res *v1.ChapterDelRes, err error) { func (c *ControllerV1) Del(ctx context.Context, req *v1.DelReq) (res *v1.DelRes, err error) {
out, err := service.Chapter().Delete(ctx, &model.ChapterDelIn{ out, err := service.Chapter().Delete(ctx, &model.ChapterDelIn{
Id: req.Id, Id: req.Id,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.ChapterDelRes{Success: out.Success}, nil return &v1.DelRes{Success: out.Success}, nil
} }

View File

@ -8,21 +8,21 @@ import (
"server/api/chapter/v1" "server/api/chapter/v1"
) )
func (c *ControllerV1) ChapterEdit(ctx context.Context, req *v1.ChapterEditReq) (res *v1.ChapterEditRes, err error) { func (c *ControllerV1) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error) {
out, err := service.Chapter().Update(ctx, &model.ChapterEditIn{ out, err := service.Chapter().Update(ctx, &model.ChapterEditIn{
BookId: req.BookId,
Content: req.Content,
Id: req.Id, Id: req.Id,
IsLocked: req.IsLocked, BookId: req.BookId,
RequiredScore: req.RequiredScore,
Sort: req.Sort,
Title: req.Title, Title: req.Title,
Sort: req.Sort,
IsLocked: req.IsLocked,
Content: req.Content,
WordCount: req.WordCount, WordCount: req.WordCount,
RequiredScore: req.RequiredScore,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.ChapterEditRes{ return &v1.EditRes{
Success: out.Success, Success: out.Success,
}, nil }, nil
} }

View File

@ -8,7 +8,7 @@ import (
"server/api/chapter/v1" "server/api/chapter/v1"
) )
func (c *ControllerV1) ChapterList(ctx context.Context, req *v1.ChapterListReq) (res *v1.ChapterListRes, err error) { func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
out, err := service.Chapter().List(ctx, &model.ChapterListIn{ out, err := service.Chapter().List(ctx, &model.ChapterListIn{
BookId: req.BookId, BookId: req.BookId,
IsLocked: req.IsLocked, IsLocked: req.IsLocked,
@ -19,7 +19,7 @@ func (c *ControllerV1) ChapterList(ctx context.Context, req *v1.ChapterListReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.ChapterListRes{ return &v1.ListRes{
List: out.List, List: out.List,
Total: out.Total, Total: out.Total,
}, nil }, nil

View File

@ -9,7 +9,7 @@ import (
"server/api/feedback/v1" "server/api/feedback/v1"
) )
func (c *ControllerV1) FeedbackAdd(ctx context.Context, req *v1.FeedbackAddReq) (res *v1.FeedbackAddRes, err error) { func (c *ControllerV1) Add(ctx context.Context, req *v1.AddReq) (res *v1.AddRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64() userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
out, err := service.Feedback().Create(ctx, &model.FeedbackAddIn{ out, err := service.Feedback().Create(ctx, &model.FeedbackAddIn{
Content: req.Content, Content: req.Content,
@ -18,7 +18,5 @@ func (c *ControllerV1) FeedbackAdd(ctx context.Context, req *v1.FeedbackAddReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.FeedbackAddRes{ return &v1.AddRes{Success: out.Success}, nil
Success: out.Success,
}, nil
} }

View File

@ -8,7 +8,7 @@ import (
"server/api/feedback/v1" "server/api/feedback/v1"
) )
func (c *ControllerV1) FeedbackList(ctx context.Context, req *v1.FeedbackListReq) (res *v1.FeedbackListRes, err error) { func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
out, err := service.Feedback().List(ctx, &model.FeedbackListIn{ out, err := service.Feedback().List(ctx, &model.FeedbackListIn{
Page: req.Page, Page: req.Page,
Size: req.Size, Size: req.Size,
@ -18,8 +18,8 @@ func (c *ControllerV1) FeedbackList(ctx context.Context, req *v1.FeedbackListReq
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &v1.FeedbackListRes{ return &v1.ListRes{
List: out.List,
Total: out.Total, Total: out.Total,
List: out.List,
}, nil }, nil
} }

View File

@ -0,0 +1,32 @@
package user
import (
"context"
v1 "server/api/user/v1"
"server/internal/model"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/frame/g"
)
func (c *ControllerV1) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error) {
// 从ctx获取userId
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
if userId == 0 {
return nil, ecode.Logout
}
out, err := service.User().Info(ctx, &model.UserInfoIn{UserId: userId})
if err != nil {
return nil, err
}
return &v1.InfoRes{
UserId: out.UserId,
Username: out.Username,
Avatar: out.Avatar,
Email: out.Email,
Points: out.Points,
}, nil
}

View File

@ -1,24 +0,0 @@
package user
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"server/internal/model"
"server/internal/service"
"server/api/user/v1"
)
func (c *ControllerV1) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) {
userId := g.RequestFromCtx(ctx).GetCtxVar("id").Int64()
info, err := service.User().Info(ctx, &model.UserInfoIn{UserId: userId})
if err != nil {
return nil, err
}
return &v1.UserInfoRes{
Username: info.Username,
Avatar: info.Avatar,
Email: info.Email,
Points: info.Points,
}, nil
}

View File

@ -29,14 +29,16 @@ type BooksColumns struct {
Status string // 状态1=连载中2=完结3=下架 Status string // 状态1=连载中2=完结3=下架
WordsCount string // 字数 WordsCount string // 字数
ChaptersCount string // 章节数 ChaptersCount string // 章节数
LatestChapterId string // 最新章节ID
Rating string // 评分0.00~10.00 Rating string // 评分0.00~10.00
ReadCount string // 阅读人数 ReadCount string // 阅读人数
CurrentReaders string // 在读人数
Tags string // 标签(逗号分隔) Tags string // 标签(逗号分隔)
CreatedAt string // 创建时间 CreatedAt string // 创建时间
UpdatedAt string // 更新时间 UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳 DeletedAt string // 软删除时间戳
IsRecommended string // 是否推荐0=否1=是 IsRecommended string // 是否推荐0=否1=是
IsFeatured string // 是否精选0=否1=是
Language string // 语言,如 zh=中文en=英文jp=日文
} }
// booksColumns holds the columns for the table books. // booksColumns holds the columns for the table books.
@ -50,14 +52,16 @@ var booksColumns = BooksColumns{
Status: "status", Status: "status",
WordsCount: "words_count", WordsCount: "words_count",
ChaptersCount: "chapters_count", ChaptersCount: "chapters_count",
LatestChapterId: "latest_chapter_id",
Rating: "rating", Rating: "rating",
ReadCount: "read_count", ReadCount: "read_count",
CurrentReaders: "current_readers",
Tags: "tags", Tags: "tags",
CreatedAt: "created_at", CreatedAt: "created_at",
UpdatedAt: "updated_at", UpdatedAt: "updated_at",
DeletedAt: "deleted_at", DeletedAt: "deleted_at",
IsRecommended: "is_recommended", IsRecommended: "is_recommended",
IsFeatured: "is_featured",
Language: "language",
} }
// NewBooksDao creates and returns a new DAO object for table data access. // NewBooksDao creates and returns a new DAO object for table data access.

View File

@ -27,6 +27,7 @@ type BookshelvesColumns struct {
LastReadChapterId string // 最后阅读章节ID LastReadChapterId string // 最后阅读章节ID
LastReadPercent string // 阅读进度百分比0.00~100.00 LastReadPercent string // 阅读进度百分比0.00~100.00
LastReadAt string // 最后阅读时间 LastReadAt string // 最后阅读时间
ReadStatus string // 阅读状态1=正在读2=已读完3=已收藏
} }
// bookshelvesColumns holds the columns for the table bookshelves. // bookshelvesColumns holds the columns for the table bookshelves.
@ -38,6 +39,7 @@ var bookshelvesColumns = BookshelvesColumns{
LastReadChapterId: "last_read_chapter_id", LastReadChapterId: "last_read_chapter_id",
LastReadPercent: "last_read_percent", LastReadPercent: "last_read_percent",
LastReadAt: "last_read_at", LastReadAt: "last_read_at",
ReadStatus: "read_status",
} }
// NewBookshelvesDao creates and returns a new DAO object for table data access. // NewBookshelvesDao creates and returns a new DAO object for table data access.

View File

@ -22,20 +22,20 @@ type CategoriesDao struct {
type CategoriesColumns struct { type CategoriesColumns struct {
Id string // 分类ID Id string // 分类ID
Name string // 分类名称 Name string // 分类名称
Type string // 分类类型1=男频, 2=女频
CreatedAt string // 创建时间 CreatedAt string // 创建时间
UpdatedAt string // 更新时间 UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳 DeletedAt string // 软删除时间戳
Channel string // 频道类型1=男频2=女频
} }
// categoriesColumns holds the columns for the table categories. // categoriesColumns holds the columns for the table categories.
var categoriesColumns = CategoriesColumns{ var categoriesColumns = CategoriesColumns{
Id: "id", Id: "id",
Name: "name", Name: "name",
Type: "type",
CreatedAt: "created_at", CreatedAt: "created_at",
UpdatedAt: "updated_at", UpdatedAt: "updated_at",
DeletedAt: "deleted_at", DeletedAt: "deleted_at",
Channel: "channel",
} }
// NewCategoriesDao creates and returns a new DAO object for table data access. // NewCategoriesDao creates and returns a new DAO object for table data access.

View File

@ -22,9 +22,9 @@ type UserPointsLogsDao struct {
type UserPointsLogsColumns struct { type UserPointsLogsColumns struct {
Id string // 积分流水ID Id string // 积分流水ID
UserId string // 用户ID UserId string // 用户ID
ChangeType string // 变动类型,例如 earn、spend、refund 等 ChangeType string // 变动类型,1=消费(spend), 2=收入(earn)
PointsChange string // 积分变化数,正数增加,负数减少 PointsChange string // 积分变化数,正数增加,负数减少
RelatedOrderId string // 关联订单ID RelatedOrderId string // 关联ID当change_type=1时为chapter_purchases.id当change_type=2时为advertisement_records.id
Description string // 变动说明 Description string // 变动说明
CreatedAt string // 变动时间 CreatedAt string // 变动时间
} }

View File

@ -0,0 +1,81 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// UserReadHistoryDao is the data access object for the table user_read_history.
type UserReadHistoryDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns UserReadHistoryColumns // columns contains all the column names of Table for convenient usage.
}
// UserReadHistoryColumns defines and stores column names for the table user_read_history.
type UserReadHistoryColumns struct {
Id string // 历史记录ID
UserId string // 用户ID
BookId string // 小说ID
ChapterId string // 最后阅读章节ID
ReadAt string // 最后阅读时间
}
// userReadHistoryColumns holds the columns for the table user_read_history.
var userReadHistoryColumns = UserReadHistoryColumns{
Id: "id",
UserId: "user_id",
BookId: "book_id",
ChapterId: "chapter_id",
ReadAt: "read_at",
}
// NewUserReadHistoryDao creates and returns a new DAO object for table data access.
func NewUserReadHistoryDao() *UserReadHistoryDao {
return &UserReadHistoryDao{
group: "default",
table: "user_read_history",
columns: userReadHistoryColumns,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *UserReadHistoryDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *UserReadHistoryDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *UserReadHistoryDao) Columns() UserReadHistoryColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *UserReadHistoryDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *UserReadHistoryDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *UserReadHistoryDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@ -11,62 +11,68 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
// ReadRecordsDao is the data access object for the table read_records. // UserReadRecordsDao is the data access object for the table user_read_records.
type ReadRecordsDao struct { type UserReadRecordsDao struct {
table string // table is the underlying table name of the DAO. table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO. group string // group is the database configuration group name of the current DAO.
columns ReadRecordsColumns // columns contains all the column names of Table for convenient usage. columns UserReadRecordsColumns // columns contains all the column names of Table for convenient usage.
} }
// ReadRecordsColumns defines and stores column names for the table read_records. // UserReadRecordsColumns defines and stores column names for the table user_read_records.
type ReadRecordsColumns struct { type UserReadRecordsColumns struct {
Id string // 记录ID Id string // 记录ID
UserId string // 用户ID UserId string // 用户ID
BookId string // 小说ID BookId string // 小说ID
ChapterId string // 章节ID ChapterId string // 章节ID
Progress string // 阅读进度百分比(0-100)
ReadAt string // 阅读时间 ReadAt string // 阅读时间
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
} }
// readRecordsColumns holds the columns for the table read_records. // userReadRecordsColumns holds the columns for the table user_read_records.
var readRecordsColumns = ReadRecordsColumns{ var userReadRecordsColumns = UserReadRecordsColumns{
Id: "id", Id: "id",
UserId: "user_id", UserId: "user_id",
BookId: "book_id", BookId: "book_id",
ChapterId: "chapter_id", ChapterId: "chapter_id",
Progress: "progress",
ReadAt: "read_at", ReadAt: "read_at",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
} }
// NewReadRecordsDao creates and returns a new DAO object for table data access. // NewUserReadRecordsDao creates and returns a new DAO object for table data access.
func NewReadRecordsDao() *ReadRecordsDao { func NewUserReadRecordsDao() *UserReadRecordsDao {
return &ReadRecordsDao{ return &UserReadRecordsDao{
group: "default", group: "default",
table: "read_records", table: "user_read_records",
columns: readRecordsColumns, columns: userReadRecordsColumns,
} }
} }
// DB retrieves and returns the underlying raw database management object of the current DAO. // DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ReadRecordsDao) DB() gdb.DB { func (dao *UserReadRecordsDao) DB() gdb.DB {
return g.DB(dao.group) return g.DB(dao.group)
} }
// Table returns the table name of the current DAO. // Table returns the table name of the current DAO.
func (dao *ReadRecordsDao) Table() string { func (dao *UserReadRecordsDao) Table() string {
return dao.table return dao.table
} }
// Columns returns all column names of the current DAO. // Columns returns all column names of the current DAO.
func (dao *ReadRecordsDao) Columns() ReadRecordsColumns { func (dao *UserReadRecordsDao) Columns() UserReadRecordsColumns {
return dao.columns return dao.columns
} }
// Group returns the database configuration group name of the current DAO. // Group returns the database configuration group name of the current DAO.
func (dao *ReadRecordsDao) Group() string { func (dao *UserReadRecordsDao) Group() string {
return dao.group return dao.group
} }
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ReadRecordsDao) Ctx(ctx context.Context) *gdb.Model { func (dao *UserReadRecordsDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx) return dao.DB().Model(dao.table).Safe().Ctx(ctx)
} }
@ -76,6 +82,6 @@ func (dao *ReadRecordsDao) Ctx(ctx context.Context) *gdb.Model {
// //
// Note: Do not commit or roll back the transaction in function f, // Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function. // as it is automatically handled by this function.
func (dao *ReadRecordsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { func (dao *UserReadRecordsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f) return dao.Ctx(ctx).Transaction(ctx, f)
} }

View File

@ -1,27 +0,0 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"server/internal/dao/internal"
)
// internalReadRecordsDao is an internal type for wrapping the internal DAO implementation.
type internalReadRecordsDao = *internal.ReadRecordsDao
// readRecordsDao is the data access object for the table read_records.
// You can define custom methods on it to extend its functionality as needed.
type readRecordsDao struct {
internalReadRecordsDao
}
var (
// ReadRecords is a globally accessible object for table read_records operations.
ReadRecords = readRecordsDao{
internal.NewReadRecordsDao(),
}
)
// Add your custom methods and functionality below.

View File

@ -0,0 +1,27 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"server/internal/dao/internal"
)
// internalUserReadHistoryDao is an internal type for wrapping the internal DAO implementation.
type internalUserReadHistoryDao = *internal.UserReadHistoryDao
// userReadHistoryDao is the data access object for the table user_read_history.
// You can define custom methods on it to extend its functionality as needed.
type userReadHistoryDao struct {
internalUserReadHistoryDao
}
var (
// UserReadHistory is a globally accessible object for table user_read_history operations.
UserReadHistory = userReadHistoryDao{
internal.NewUserReadHistoryDao(),
}
)
// Add your custom methods and functionality below.

View File

@ -0,0 +1,27 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"server/internal/dao/internal"
)
// internalUserReadRecordsDao is an internal type for wrapping the internal DAO implementation.
type internalUserReadRecordsDao = *internal.UserReadRecordsDao
// userReadRecordsDao is the data access object for the table user_read_records.
// You can define custom methods on it to extend its functionality as needed.
type userReadRecordsDao struct {
internalUserReadRecordsDao
}
var (
// UserReadRecords is a globally accessible object for table user_read_records operations.
UserReadRecords = userReadRecordsDao{
internal.NewUserReadRecordsDao(),
}
)
// Add your custom methods and functionality below.

View File

@ -0,0 +1,268 @@
package author
import (
"context"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/service"
"server/utility/ecode"
"server/utility/encrypt"
"github.com/gogf/gf/v2/database/gdb"
)
type sAuthor struct{}
func New() service.IAuthor {
return &sAuthor{}
}
func init() {
service.RegisterAuthor(New())
}
// List retrieves a paginated list of authors
func (s *sAuthor) List(ctx context.Context, in *model.AuthorListIn) (out *model.AuthorListOut, err error) {
out = &model.AuthorListOut{}
m := dao.Authors.Ctx(ctx)
if in.PenName != "" {
m = m.Where(dao.Authors.Columns().PenName+" like ?", "%"+in.PenName+"%")
}
if in.Status != 0 {
m = m.Where(dao.Authors.Columns().Status, in.Status)
}
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return
}
return
}
// Create adds a new author
func (s *sAuthor) Create(ctx context.Context, in *model.AuthorAddIn) (out *model.AuthorCRUDOut, err error) {
// 开启事务,确保用户和作者同时写入
err = dao.Authors.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 检查该 userId 是否已存在作者
exist, err := dao.Authors.Ctx(ctx).TX(tx).
Where(dao.Authors.Columns().UserId, in.UserId).
Exist()
if err != nil {
return ecode.Fail.Sub("author_query_failed")
}
if exist {
return ecode.Params.Sub("author_user_exists")
}
// 检查用户是否存在
userExist, err := dao.Users.Ctx(ctx).TX(tx).
Where(dao.Users.Columns().Id, in.UserId).
Exist()
if err != nil {
return ecode.Fail.Sub("author_query_failed")
}
if !userExist {
// 不存在则创建用户,用户名用笔名,密码默认 Aa123456
hash, err := encrypt.EncryptPassword("Aa123456")
if err != nil {
return ecode.Fail.Sub("password_encryption_failed")
}
result, err := dao.Users.Ctx(ctx).TX(tx).Data(do.Users{
Username: in.PenName,
PasswordHash: hash,
}).InsertAndGetId()
if err != nil {
return ecode.Fail.Sub("author_create_failed")
}
in.UserId = result
}
// 创建作者
_, err = dao.Authors.Ctx(ctx).TX(tx).Data(do.Authors{
UserId: in.UserId,
PenName: in.PenName,
Bio: in.Bio,
Status: in.Status,
}).Insert()
if err != nil {
return ecode.Fail.Sub("author_create_failed")
}
return nil
})
if err != nil {
return nil, err
}
return &model.AuthorCRUDOut{Success: true}, nil
}
// Update edits an author
func (s *sAuthor) Update(ctx context.Context, in *model.AuthorEditIn) (out *model.AuthorCRUDOut, err error) {
exist, err := dao.Authors.Ctx(ctx).
WherePri(in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("author_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("author_not_found")
}
_, err = dao.Authors.Ctx(ctx).
WherePri(in.Id).
Data(do.Authors{
PenName: in.PenName,
Bio: in.Bio,
Status: in.Status,
}).Update()
if err != nil {
return nil, ecode.Fail.Sub("author_update_failed")
}
return &model.AuthorCRUDOut{Success: true}, nil
}
// Delete removes an author by id
func (s *sAuthor) Delete(ctx context.Context, in *model.AuthorDelIn) (out *model.AuthorCRUDOut, err error) {
exist, err := dao.Authors.Ctx(ctx).
WherePri(in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("author_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("author_not_found")
}
// 开启事务,删除作者及相关数据
err = dao.Authors.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 1. 删除作者相关的用户关注记录
_, err := dao.UserFollowAuthors.Ctx(ctx).TX(tx).
Where(dao.UserFollowAuthors.Columns().AuthorId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("follow_author_delete_failed")
}
// 2. 删除作者相关的书籍(这里需要递归删除书籍相关的所有数据)
// 先查询该作者的所有书籍ID
var bookIds []int64
err = dao.Books.Ctx(ctx).TX(tx).
Where(dao.Books.Columns().AuthorId, in.Id).
Fields("id").
Scan(&bookIds)
if err != nil {
return ecode.Fail.Sub("book_query_failed")
}
if len(bookIds) > 0 {
// 删除书籍相关的章节
_, err = dao.Chapters.Ctx(ctx).TX(tx).
WhereIn(dao.Chapters.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("chapter_delete_failed")
}
// 删除书籍相关的用户阅读记录
_, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
WhereIn(dao.UserReadRecords.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("read_record_delete_failed")
}
// 删除书籍相关的用户阅读历史
_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
WhereIn(dao.UserReadHistory.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("history_delete_failed")
}
// 删除书籍相关的用户书架
_, err = dao.Bookshelves.Ctx(ctx).TX(tx).
WhereIn(dao.Bookshelves.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("bookshelf_delete_failed")
}
// 删除书籍相关的用户评分
_, err = dao.BookRatings.Ctx(ctx).TX(tx).
WhereIn(dao.BookRatings.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("rating_delete_failed")
}
// 删除书籍相关的章节购买记录
_, err = dao.UserChapterPurchases.Ctx(ctx).TX(tx).
WhereIn(dao.UserChapterPurchases.Columns().BookId, bookIds).
Delete()
if err != nil {
return ecode.Fail.Sub("purchase_delete_failed")
}
// 最后删除书籍
_, err = dao.Books.Ctx(ctx).TX(tx).
Where(dao.Books.Columns().AuthorId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("book_delete_failed")
}
}
// 3. 最后删除作者
_, err = dao.Authors.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
if err != nil {
return ecode.Fail.Sub("author_delete_failed")
}
return nil
})
if err != nil {
return nil, err
}
return &model.AuthorCRUDOut{Success: true}, nil
}
// Apply 允许用户申请成为作者
func (s *sAuthor) Apply(ctx context.Context, in *model.AuthorApplyIn) (out *model.AuthorApplyOut, err error) {
userIdVal := ctx.Value("id")
userId, ok := userIdVal.(int64)
if !ok || userId == 0 {
return nil, ecode.Fail.Sub("user_id_invalid")
}
exist, err := dao.Authors.Ctx(ctx).
Where(dao.Authors.Columns().UserId, userId).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("author_query_failed")
}
if exist {
return nil, ecode.Params.Sub("author_user_exists")
}
if _, err := dao.Authors.Ctx(ctx).Data(do.Authors{
UserId: userId,
PenName: in.PenName,
Bio: in.Bio,
Status: 1, // 默认正常
}).Insert(); err != nil {
return nil, ecode.Fail.Sub("author_create_failed")
}
return &model.AuthorApplyOut{Success: true}, nil
}
func (s *sAuthor) Detail(ctx context.Context, in *model.AuthorDetailIn) (out *model.AuthorDetailOut, err error) {
out = &model.AuthorDetailOut{}
exist, err := dao.Authors.Ctx(ctx).
WherePri(in.AuthorId).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("author_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("author_not_found")
}
if err = dao.Authors.Ctx(ctx).WherePri(in.AuthorId).WithAll().Scan(&out); err != nil {
return nil, ecode.Fail.Sub("author_query_failed")
}
return out, nil
}

View File

@ -7,6 +7,8 @@ import (
"server/internal/model/do" "server/internal/model/do"
"server/internal/service" "server/internal/service"
"server/utility/ecode" "server/utility/ecode"
"github.com/gogf/gf/v2/database/gdb"
) )
type sBook struct{} type sBook struct{}
@ -38,7 +40,10 @@ func (s *sBook) List(ctx context.Context, in *model.BookListIn) (out *model.Book
if in.IsRecommended != 0 { if in.IsRecommended != 0 {
m = m.Where(dao.Books.Columns().IsRecommended, in.IsRecommended) m = m.Where(dao.Books.Columns().IsRecommended, in.IsRecommended)
} }
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil { if in.Sort != "" {
m = m.Order(in.Sort)
}
if err = m.Page(in.Page, in.Size).WithAll().ScanAndCount(&out.List, &out.Total, false); err != nil {
return return
} }
return return
@ -127,12 +132,633 @@ func (s *sBook) Delete(ctx context.Context, in *model.BookDelIn) (out *model.Boo
return nil, ecode.NotFound.Sub("book_not_found") return nil, ecode.NotFound.Sub("book_not_found")
} }
_, err = dao.Books.Ctx(ctx).WherePri(in.Id).Delete() // 开启事务,删除书籍及相关数据
err = dao.Books.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 1. 删除书籍相关的章节
_, err := dao.Chapters.Ctx(ctx).TX(tx).
Where(dao.Chapters.Columns().BookId, in.Id).
Delete()
if err != nil { if err != nil {
return nil, ecode.Fail.Sub("book_delete_failed") return ecode.Fail.Sub("chapter_delete_failed")
}
// 2. 删除书籍相关的用户阅读记录
_, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().BookId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("read_record_delete_failed")
}
// 3. 删除书籍相关的用户阅读历史
_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
Where(dao.UserReadHistory.Columns().BookId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("history_delete_failed")
}
// 4. 删除书籍相关的用户书架
_, err = dao.Bookshelves.Ctx(ctx).TX(tx).
Where(dao.Bookshelves.Columns().BookId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("bookshelf_delete_failed")
}
// 5. 删除书籍相关的用户评分
_, err = dao.BookRatings.Ctx(ctx).TX(tx).
Where(dao.BookRatings.Columns().BookId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("rating_delete_failed")
}
// 6. 删除书籍相关的章节购买记录
_, err = dao.UserChapterPurchases.Ctx(ctx).TX(tx).
Where(dao.UserChapterPurchases.Columns().BookId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("purchase_delete_failed")
}
// 7. 最后删除书籍
_, err = dao.Books.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
if err != nil {
return ecode.Fail.Sub("book_delete_failed")
}
return nil
})
if err != nil {
return nil, err
} }
return &model.BookCRUDOut{ return &model.BookCRUDOut{
Success: true, Success: true,
}, nil }, nil
} }
// AppList retrieves book list for app
func (s *sBook) AppList(ctx context.Context, in *model.BookAppListIn) (out *model.BookAppListOut, err error) {
out = &model.BookAppListOut{}
// 构建查询条件使用WithAll查询关联的作者信息
m := dao.Books.Ctx(ctx).WithAll()
// 书名模糊搜索
if in.Title != "" {
m = m.Where(dao.Books.Columns().Title+" like ?", "%"+in.Title+"%")
}
// 分类筛选
if in.CategoryId != 0 {
m = m.Where(dao.Books.Columns().CategoryId, in.CategoryId)
}
// 推荐筛选
if in.IsRecommended {
m = m.Where(dao.Books.Columns().IsRecommended, 1)
}
// 最新筛选(按创建时间倒序)
if in.IsLatest == 1 {
m = m.OrderDesc(dao.Books.Columns().CreatedAt)
}
if in.AuthorId != 0 {
m = m.Where(dao.Books.Columns().AuthorId, in.AuthorId)
}
if in.IsFeatured {
m = m.Where(dao.Books.Columns().IsFeatured, 1)
}
if in.Sort != "" {
m = m.Order(in.Sort)
}
// 执行分页查询
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
// 用户评分批量查询
if in.UserId > 0 && len(out.List) > 0 {
bookIds := make([]int64, 0, len(out.List))
for _, item := range out.List {
bookIds = append(bookIds, item.Id)
}
// 查询用户对这些书的评分
type ratingRow struct {
BookId int64 `json:"bookId"`
Score float64 `json:"score"`
}
ratings := make([]ratingRow, 0)
err = dao.BookRatings.Ctx(ctx).
Fields("book_id, score").
Where(dao.BookRatings.Columns().UserId, in.UserId).
WhereIn(dao.BookRatings.Columns().BookId, bookIds).
Scan(&ratings)
if err == nil && len(ratings) > 0 {
ratingMap := make(map[int64]float64, len(ratings))
for _, r := range ratings {
ratingMap[r.BookId] = r.Score
}
for i := range out.List {
if score, ok := ratingMap[out.List[i].Id]; ok {
out.List[i].HasRated = true
out.List[i].MyRating = score
} else {
out.List[i].HasRated = false
out.List[i].MyRating = 0
}
}
} else {
for i := range out.List {
out.List[i].HasRated = false
out.List[i].MyRating = 0
}
}
}
return out, nil
}
// AppRate rates a book for app
func (s *sBook) AppRate(ctx context.Context, in *model.BookAppRateIn) (out *model.BookAppRateOut, err error) {
out = &model.BookAppRateOut{}
// 必须指定用户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")
}
// 验证评分范围1-10分
if in.Rating < 1 || in.Rating > 10 {
return nil, ecode.Fail.Sub("rating_invalid")
}
// 检查书籍是否存在
exist, err := dao.Books.Ctx(ctx).Where(dao.Books.Columns().Id, in.BookId).Exist()
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("book_not_found")
}
// 检查用户是否存在
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")
}
// 开启事务处理评分
if err := dao.BookRatings.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 检查是否已经评分过
exist, err := dao.BookRatings.Ctx(ctx).TX(tx).
Where(dao.BookRatings.Columns().UserId, in.UserId).
Where(dao.BookRatings.Columns().BookId, in.BookId).
Exist()
if err != nil {
return ecode.Fail.Sub("rating_query_failed")
}
if exist {
// 更新现有评分
_, err = dao.BookRatings.Ctx(ctx).TX(tx).
Where(dao.BookRatings.Columns().UserId, in.UserId).
Where(dao.BookRatings.Columns().BookId, in.BookId).
Data(do.BookRatings{
Score: in.Rating,
}).Update()
if err != nil {
return ecode.Fail.Sub("rating_update_failed")
}
} else {
// 创建新评分记录
_, err = dao.BookRatings.Ctx(ctx).TX(tx).Data(do.BookRatings{
UserId: in.UserId,
BookId: in.BookId,
Score: in.Rating,
}).Insert()
if err != nil {
return ecode.Fail.Sub("rating_create_failed")
}
}
// 重新计算书籍平均评分
var avgRating float64
err = dao.BookRatings.Ctx(ctx).TX(tx).
Where(dao.BookRatings.Columns().BookId, in.BookId).
Fields("AVG(score) as avg_rating").
Scan(&avgRating)
if err != nil {
return ecode.Fail.Sub("rating_calculation_failed")
}
// 更新书籍的平均评分
_, err = dao.Books.Ctx(ctx).TX(tx).
Where(dao.Books.Columns().Id, in.BookId).
Data(do.Books{
Rating: avgRating,
}).Update()
if err != nil {
return ecode.Fail.Sub("book_rating_update_failed")
}
return nil
}); err != nil {
return nil, err
}
out.Success = true
return out, nil
}
// AppDetail retrieves book detail for app
func (s *sBook) AppDetail(ctx context.Context, in *model.BookAppDetailIn) (out *model.BookAppDetailOut, err error) {
out = &model.BookAppDetailOut{}
// 必须指定书籍ID
if in.Id == 0 {
return nil, ecode.Fail.Sub("book_id_required")
}
// 构建查询条件
m := dao.Books.Ctx(ctx)
m = m.Where(dao.Books.Columns().Id, in.Id)
// 执行查询
if err = m.Scan(out); err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
// 检查书籍是否存在
if out.Id == 0 {
return nil, ecode.NotFound.Sub("book_not_found")
}
// 如果用户已登录,查询阅读进度
if in.UserId > 0 {
// 查询用户对该书籍的历史记录
var historyRecord struct {
ChapterId int64 `json:"chapterId"`
ReadAt string `json:"readAt"`
}
err = dao.UserReadHistory.Ctx(ctx).
Fields("chapter_id, read_at").
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
Where(dao.UserReadHistory.Columns().BookId, in.Id).
OrderDesc(dao.UserReadHistory.Columns().ReadAt).
Scan(&historyRecord)
if err == nil && historyRecord.ChapterId > 0 {
// 用户读过这本书
out.HasRead = true
out.LastChapterId = historyRecord.ChapterId
out.LastReadAt = historyRecord.ReadAt
// 计算阅读进度:总章节数 vs 已读章节数
totalChapters, err := dao.Chapters.Ctx(ctx).
Where(dao.Chapters.Columns().BookId, in.Id).
Count()
if err == nil && totalChapters > 0 {
readChapters, err := dao.UserReadRecords.Ctx(ctx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.Id).
Where(dao.UserReadRecords.Columns().ChapterId, ">", 0). // 只统计有章节ID的记录
Count()
if err == nil {
out.ReadProgress = int(float64(readChapters) / float64(totalChapters) * 100)
if out.ReadProgress > 100 {
out.ReadProgress = 100
}
}
}
}
}
return out, nil
}
func (s *sBook) MyList(ctx context.Context, in *model.MyBookListIn) (out *model.MyBookListOut, err error) {
out = &model.MyBookListOut{}
// 验证用户ID
if in.UserId == 0 {
return nil, ecode.Fail.Sub("user_id_required")
}
// 验证类型参数
if in.Type < 1 || in.Type > 3 {
return nil, ecode.Fail.Sub("type_invalid")
}
var list []model.MyBookItem
var total int
switch in.Type {
case 1: // 正在读
// 查询书架表中read_status=1的记录
var bookshelves []struct {
BookId int64 `json:"bookId"`
LastReadPercent float64 `json:"lastReadPercent"`
LastReadAt string `json:"lastReadAt"`
}
if in.Sort != "" {
err = dao.Bookshelves.Ctx(ctx).
Fields("book_id, last_read_percent, last_read_at").
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().ReadStatus, 1).
Order(in.Sort).
Page(in.Page, in.Size).
ScanAndCount(&bookshelves, &total, false)
} else {
err = dao.Bookshelves.Ctx(ctx).
Fields("book_id, last_read_percent, last_read_at").
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().ReadStatus, 1).
Page(in.Page, in.Size).
ScanAndCount(&bookshelves, &total, false)
}
if err != nil {
return nil, ecode.Fail.Sub("bookshelf_query_failed")
}
// 获取书籍详细信息
if len(bookshelves) > 0 {
bookIds := make([]int64, 0, len(bookshelves))
for _, bs := range bookshelves {
bookIds = append(bookIds, bs.BookId)
}
var books []model.Book
err = dao.Books.Ctx(ctx).
WhereIn(dao.Books.Columns().Id, bookIds).
Scan(&books)
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
// 构建书籍ID到书架信息的映射
bookshelfMap := make(map[int64]struct {
LastReadPercent float64
LastReadAt string
})
for _, bs := range bookshelves {
bookshelfMap[bs.BookId] = struct {
LastReadPercent float64
LastReadAt string
}{
LastReadPercent: bs.LastReadPercent,
LastReadAt: bs.LastReadAt,
}
}
// 构建返回列表
for _, book := range books {
bsInfo := bookshelfMap[book.Id]
list = append(list, model.MyBookItem{
Id: book.Id,
Title: book.Title,
CoverUrl: book.CoverUrl,
Description: book.Description,
Progress: int(bsInfo.LastReadPercent),
IsInShelf: true,
LastReadAt: bsInfo.LastReadAt,
Status: book.Status,
AuthorId: book.AuthorId,
CategoryId: book.CategoryId,
})
}
}
case 2: // 已读完
// 查询书架表中read_status=2的记录
var bookshelves []struct {
BookId int64 `json:"bookId"`
LastReadPercent float64 `json:"lastReadPercent"`
LastReadAt string `json:"lastReadAt"`
}
if in.Sort != "" {
err = dao.Bookshelves.Ctx(ctx).
Fields("book_id, last_read_percent, last_read_at").
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().ReadStatus, 2).
Order(in.Sort).
Page(in.Page, in.Size).
ScanAndCount(&bookshelves, &total, false)
} else {
err = dao.Bookshelves.Ctx(ctx).
Fields("book_id, last_read_percent, last_read_at").
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().ReadStatus, 2).
Page(in.Page, in.Size).
ScanAndCount(&bookshelves, &total, false)
}
if err != nil {
return nil, ecode.Fail.Sub("bookshelf_query_failed")
}
// 获取书籍详细信息
if len(bookshelves) > 0 {
bookIds := make([]int64, 0, len(bookshelves))
for _, bs := range bookshelves {
bookIds = append(bookIds, bs.BookId)
}
var books []model.Book
err = dao.Books.Ctx(ctx).
WhereIn(dao.Books.Columns().Id, bookIds).
Scan(&books)
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
// 构建书籍ID到书架信息的映射
bookshelfMap := make(map[int64]struct {
LastReadPercent float64
LastReadAt string
})
for _, bs := range bookshelves {
bookshelfMap[bs.BookId] = struct {
LastReadPercent float64
LastReadAt string
}{
LastReadPercent: bs.LastReadPercent,
LastReadAt: bs.LastReadAt,
}
}
// 构建返回列表
for _, book := range books {
bsInfo := bookshelfMap[book.Id]
list = append(list, model.MyBookItem{
Id: book.Id,
Title: book.Title,
CoverUrl: book.CoverUrl,
Description: book.Description,
Progress: int(bsInfo.LastReadPercent),
IsInShelf: true,
LastReadAt: bsInfo.LastReadAt,
Status: book.Status,
AuthorId: book.AuthorId,
CategoryId: book.CategoryId,
})
}
}
case 3: // 历史记录
// 查询user_read_history表
var histories []struct {
BookId int64 `json:"bookId"`
ChapterId int64 `json:"chapterId"`
ReadAt string `json:"readAt"`
}
if in.Sort != "" {
err = dao.UserReadHistory.Ctx(ctx).
Fields("book_id, chapter_id, read_at").
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
Order(in.Sort).
Page(in.Page, in.Size).
ScanAndCount(&histories, &total, false)
} else {
err = dao.UserReadHistory.Ctx(ctx).
Fields("book_id, chapter_id, read_at").
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
OrderDesc(dao.UserReadHistory.Columns().ReadAt).
Page(in.Page, in.Size).
ScanAndCount(&histories, &total, false)
}
if err != nil {
return nil, ecode.Fail.Sub("history_query_failed")
}
// 获取书籍详细信息
if len(histories) > 0 {
bookIds := make([]int64, 0, len(histories))
for _, history := range histories {
bookIds = append(bookIds, history.BookId)
}
var books []model.Book
err = dao.Books.Ctx(ctx).
WhereIn(dao.Books.Columns().Id, bookIds).
Scan(&books)
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
// 构建书籍ID到历史信息的映射
historyMap := make(map[int64]struct {
ChapterId int64
ReadAt string
})
for _, history := range histories {
historyMap[history.BookId] = struct {
ChapterId int64
ReadAt string
}{
ChapterId: history.ChapterId,
ReadAt: history.ReadAt,
}
}
// 检查是否在书架中
var bookshelves []struct {
BookId int64 `json:"bookId"`
}
err = dao.Bookshelves.Ctx(ctx).
Fields("book_id").
Where(dao.Bookshelves.Columns().UserId, in.UserId).
WhereIn(dao.Bookshelves.Columns().BookId, bookIds).
Scan(&bookshelves)
if err != nil {
return nil, ecode.Fail.Sub("bookshelf_query_failed")
}
// 构建书架ID集合
shelfBookIds := make(map[int64]bool)
for _, bs := range bookshelves {
shelfBookIds[bs.BookId] = true
}
// 构建返回列表
for _, book := range books {
historyInfo := historyMap[book.Id]
list = append(list, model.MyBookItem{
Id: book.Id,
Title: book.Title,
CoverUrl: book.CoverUrl,
Description: book.Description,
Progress: 0, // 历史记录不显示进度
IsInShelf: shelfBookIds[book.Id],
LastReadAt: historyInfo.ReadAt,
Status: book.Status,
AuthorId: book.AuthorId,
CategoryId: book.CategoryId,
})
}
}
default:
// 返回空列表
}
out = &model.MyBookListOut{
Total: total,
List: list,
}
return
}
// SetFeatured: 单独修改书籍的精选状态
func (s *sBook) SetFeatured(ctx context.Context, in *model.BookSetFeaturedIn) (out *model.BookCRUDOut, err error) {
// 检查书籍是否存在
exist, err := dao.Books.Ctx(ctx).WherePri(in.Id).Exist()
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("book_not_found")
}
_, err = dao.Books.Ctx(ctx).WherePri(in.Id).Data(do.Books{
IsFeatured: in.IsFeatured,
}).Update()
if err != nil {
return nil, ecode.Fail.Sub("book_update_failed")
}
return &model.BookCRUDOut{Success: true}, nil
}
// SetRecommended: 单独修改书籍的推荐状态
func (s *sBook) SetRecommended(ctx context.Context, in *model.BookSetRecommendedIn) (out *model.BookCRUDOut, err error) {
// 检查书籍是否存在
exist, err := dao.Books.Ctx(ctx).WherePri(in.Id).Exist()
if err != nil {
return nil, ecode.Fail.Sub("book_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("book_not_found")
}
_, err = dao.Books.Ctx(ctx).WherePri(in.Id).Data(do.Books{
IsRecommended: in.IsRecommended,
}).Update()
if err != nil {
return nil, ecode.Fail.Sub("book_update_failed")
}
return &model.BookCRUDOut{Success: true}, nil
}

View File

@ -0,0 +1,57 @@
package bookshelve
import (
"context"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/service"
"server/utility/ecode"
)
type sBookshelve struct{}
func New() service.IBookshelve {
return &sBookshelve{}
}
func init() {
service.RegisterBookshelve(New())
}
// Add 添加书架
func (s *sBookshelve) Add(ctx context.Context, in *model.BookshelveAddIn) (out *model.BookshelveCRUDOut, err error) {
exist, err := dao.Bookshelves.Ctx(ctx).
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().BookId, in.BookId).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("bookshelve_query_failed")
}
if exist {
return nil, ecode.Params.Sub("bookshelve_exists")
}
if _, err := dao.Bookshelves.Ctx(ctx).Data(do.Bookshelves{
UserId: in.UserId,
BookId: in.BookId,
ReadStatus: 1, // 默认为正在读
}).Insert(); err != nil {
return nil, ecode.Fail.Sub("bookshelve_create_failed")
}
return &model.BookshelveCRUDOut{Success: true}, nil
}
// Delete 批量删除书架
func (s *sBookshelve) Delete(ctx context.Context, in *model.BookshelveDelIn) (out *model.BookshelveCRUDOut, err error) {
if len(in.BookIds) == 0 {
return nil, ecode.Params.Sub("bookshelve_bookids_empty")
}
_, err = dao.Bookshelves.Ctx(ctx).
Where(dao.Bookshelves.Columns().UserId, in.UserId).
WhereIn(dao.Bookshelves.Columns().BookId, in.BookIds).
Delete()
if err != nil {
return nil, ecode.Fail.Sub("bookshelve_delete_failed")
}
return &model.BookshelveCRUDOut{Success: true}, nil
}

View File

@ -7,6 +7,8 @@ import (
"server/internal/model/do" "server/internal/model/do"
"server/internal/service" "server/internal/service"
"server/utility/ecode" "server/utility/ecode"
"github.com/gogf/gf/v2/database/gdb"
) )
type sCategory struct { type sCategory struct {
@ -24,8 +26,8 @@ func init() {
func (s *sCategory) List(ctx context.Context, in *model.CategoryListIn) (out *model.CategoryListOut, err error) { func (s *sCategory) List(ctx context.Context, in *model.CategoryListIn) (out *model.CategoryListOut, err error) {
out = &model.CategoryListOut{} out = &model.CategoryListOut{}
m := dao.Categories.Ctx(ctx) m := dao.Categories.Ctx(ctx)
if in.Type != 0 { if in.Channel != 0 {
m = m.Where(dao.Categories.Columns().Type, in.Type) m = m.Where(dao.Categories.Columns().Channel, in.Channel)
} }
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil { if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return return
@ -34,9 +36,6 @@ func (s *sCategory) List(ctx context.Context, in *model.CategoryListIn) (out *mo
} }
func (s *sCategory) Create(ctx context.Context, in *model.CategoryAddIn) (out *model.CategoryCRUDOut, err error) { func (s *sCategory) Create(ctx context.Context, in *model.CategoryAddIn) (out *model.CategoryCRUDOut, err error) {
if in.Type != 1 && in.Type != 2 {
return nil, ecode.Params.Sub("category_type_invalid")
}
exist, err := dao.Categories.Ctx(ctx). exist, err := dao.Categories.Ctx(ctx).
Where(dao.Categories.Columns().Name, in.Name). Where(dao.Categories.Columns().Name, in.Name).
Exist() Exist()
@ -49,7 +48,7 @@ func (s *sCategory) Create(ctx context.Context, in *model.CategoryAddIn) (out *m
if _, err := dao.Categories.Ctx(ctx).Data(do.Categories{ if _, err := dao.Categories.Ctx(ctx).Data(do.Categories{
Name: in.Name, Name: in.Name,
Type: in.Type, Channel: in.Channel,
}).Insert(); err != nil { }).Insert(); err != nil {
return nil, ecode.Fail.Sub("category_create_failed") return nil, ecode.Fail.Sub("category_create_failed")
} }
@ -60,9 +59,6 @@ func (s *sCategory) Create(ctx context.Context, in *model.CategoryAddIn) (out *m
} }
func (s *sCategory) Update(ctx context.Context, in *model.CategoryEditIn) (out *model.CategoryCRUDOut, err error) { func (s *sCategory) Update(ctx context.Context, in *model.CategoryEditIn) (out *model.CategoryCRUDOut, err error) {
if in.Type != 1 && in.Type != 2 {
return nil, ecode.Params.Sub("category_type_invalid")
}
exist, err := dao.Categories.Ctx(ctx). exist, err := dao.Categories.Ctx(ctx).
WherePri(in.Id). WherePri(in.Id).
Exist() Exist()
@ -89,7 +85,7 @@ func (s *sCategory) Update(ctx context.Context, in *model.CategoryEditIn) (out *
WherePri(in.Id). WherePri(in.Id).
Data(do.Categories{ Data(do.Categories{
Name: in.Name, Name: in.Name,
Type: in.Type, Channel: in.Channel,
}).Update() }).Update()
if err != nil { if err != nil {
return nil, ecode.Fail.Sub("category_update_failed") return nil, ecode.Fail.Sub("category_update_failed")
@ -111,10 +107,31 @@ func (s *sCategory) Delete(ctx context.Context, in *model.CategoryDelIn) (out *m
return nil, ecode.NotFound.Sub("category_not_found") return nil, ecode.NotFound.Sub("category_not_found")
} }
// Soft delete category // 开启事务,检查是否有书籍使用了这个分类
_, err = dao.Categories.Ctx(ctx).WherePri(in.Id).Delete() err = dao.Categories.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 检查是否有书籍使用了这个分类
bookCount, err := dao.Books.Ctx(ctx).TX(tx).
Where(dao.Books.Columns().CategoryId, in.Id).
Count()
if err != nil { if err != nil {
return nil, ecode.Fail.Sub("category_delete_failed") return ecode.Fail.Sub("book_query_failed")
}
if bookCount > 0 {
return ecode.Fail.Sub("category_in_use")
}
// 删除分类
_, err = dao.Categories.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
if err != nil {
return ecode.Fail.Sub("category_delete_failed")
}
return nil
})
if err != nil {
return nil, err
} }
return &model.CategoryCRUDOut{ return &model.CategoryCRUDOut{

View File

@ -5,8 +5,12 @@ import (
"server/internal/dao" "server/internal/dao"
"server/internal/model" "server/internal/model"
"server/internal/model/do" "server/internal/model/do"
"server/internal/model/entity"
"server/internal/service" "server/internal/service"
"server/utility/ecode" "server/utility/ecode"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/gtime"
) )
type sChapter struct{} type sChapter struct{}
@ -38,6 +42,7 @@ func (s *sChapter) List(ctx context.Context, in *model.ChapterListIn) (out *mode
return return
} }
// Create creates a new chapter
func (s *sChapter) Create(ctx context.Context, in *model.ChapterAddIn) (out *model.ChapterCRUDOut, err error) { func (s *sChapter) Create(ctx context.Context, in *model.ChapterAddIn) (out *model.ChapterCRUDOut, err error) {
if _, err := dao.Chapters.Ctx(ctx).Data(do.Chapters{ if _, err := dao.Chapters.Ctx(ctx).Data(do.Chapters{
BookId: in.BookId, BookId: in.BookId,
@ -53,6 +58,7 @@ func (s *sChapter) Create(ctx context.Context, in *model.ChapterAddIn) (out *mod
return &model.ChapterCRUDOut{Success: true}, nil return &model.ChapterCRUDOut{Success: true}, nil
} }
// Update updates an existing chapter
func (s *sChapter) Update(ctx context.Context, in *model.ChapterEditIn) (out *model.ChapterCRUDOut, err error) { func (s *sChapter) Update(ctx context.Context, in *model.ChapterEditIn) (out *model.ChapterCRUDOut, err error) {
exist, err := dao.Chapters.Ctx(ctx). exist, err := dao.Chapters.Ctx(ctx).
WherePri(in.Id). WherePri(in.Id).
@ -80,6 +86,7 @@ func (s *sChapter) Update(ctx context.Context, in *model.ChapterEditIn) (out *mo
return &model.ChapterCRUDOut{Success: true}, nil return &model.ChapterCRUDOut{Success: true}, nil
} }
// Delete deletes a chapter by ID
func (s *sChapter) Delete(ctx context.Context, in *model.ChapterDelIn) (out *model.ChapterCRUDOut, err error) { func (s *sChapter) Delete(ctx context.Context, in *model.ChapterDelIn) (out *model.ChapterCRUDOut, err error) {
exist, err := dao.Chapters.Ctx(ctx). exist, err := dao.Chapters.Ctx(ctx).
WherePri(in.Id). WherePri(in.Id).
@ -90,9 +97,431 @@ func (s *sChapter) Delete(ctx context.Context, in *model.ChapterDelIn) (out *mod
if !exist { if !exist {
return nil, ecode.NotFound.Sub("chapter_not_found") return nil, ecode.NotFound.Sub("chapter_not_found")
} }
_, err = dao.Chapters.Ctx(ctx).WherePri(in.Id).Delete()
// 开启事务,删除章节及相关数据
err = dao.Chapters.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 1. 删除章节相关的用户阅读记录
_, err := dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().ChapterId, in.Id).
Delete()
if err != nil { if err != nil {
return nil, ecode.Fail.Sub("chapter_delete_failed") return ecode.Fail.Sub("read_record_delete_failed")
} }
// 2. 删除章节相关的购买记录
_, err = dao.UserChapterPurchases.Ctx(ctx).TX(tx).
Where(dao.UserChapterPurchases.Columns().ChapterId, in.Id).
Delete()
if err != nil {
return ecode.Fail.Sub("purchase_delete_failed")
}
// 3. 最后删除章节
_, err = dao.Chapters.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
if err != nil {
return ecode.Fail.Sub("chapter_delete_failed")
}
return nil
})
if err != nil {
return nil, err
}
return &model.ChapterCRUDOut{Success: true}, nil return &model.ChapterCRUDOut{Success: true}, nil
} }
// AppList retrieves chapter list for app without content
func (s *sChapter) AppList(ctx context.Context, in *model.ChapterAppListIn) (out *model.ChapterAppListOut, err error) {
out = &model.ChapterAppListOut{}
// 构建查询条件
m := dao.Chapters.Ctx(ctx)
// 必须指定 bookId
if in.BookId == 0 {
return nil, ecode.Fail.Sub("chapter_book_id_required")
}
m = m.Where(dao.Chapters.Columns().BookId, in.BookId)
// 根据 isDesc 字段排序
if in.IsDesc {
m = m.OrderDesc(dao.Chapters.Columns().Sort)
}
// 执行分页查询,获取列表和总数
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return nil, ecode.Fail.Sub("chapter_query_failed")
}
// 如果指定了用户ID查询阅读进度
if in.UserId > 0 {
// 获取所有章节ID
chapterIds := make([]int64, 0, len(out.List))
for _, item := range out.List {
chapterIds = append(chapterIds, item.Id)
}
if len(chapterIds) > 0 {
// 查询阅读记录
readRecords := make([]struct {
ChapterId int64 `json:"chapterId"`
ReadAt *gtime.Time `json:"readAt"`
}, 0)
err = dao.UserReadRecords.Ctx(ctx).
Fields("chapter_id, read_at").
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
WhereIn(dao.UserReadRecords.Columns().ChapterId, chapterIds).
Scan(&readRecords)
if err != nil {
return nil, ecode.Fail.Sub("read_record_query_failed")
}
// 构建阅读记录映射
readMap := make(map[int64]*gtime.Time)
for _, record := range readRecords {
readMap[record.ChapterId] = record.ReadAt
}
// 为每个章节设置阅读进度
for i := range out.List {
if readAt, exists := readMap[out.List[i].Id]; exists {
out.List[i].ReadAt = readAt
out.List[i].ReadProgress = 100 // 有阅读记录表示已读
} else {
out.List[i].ReadProgress = 0 // 未读
out.List[i].ReadAt = nil
}
}
}
}
return out, nil
}
// AppDetail retrieves chapter detail for app
func (s *sChapter) AppDetail(ctx context.Context, in *model.ChapterAppDetailIn) (out *model.ChapterAppDetailOut, err error) {
out = &model.ChapterAppDetailOut{}
// 构建查询条件
m := dao.Chapters.Ctx(ctx)
// 必须指定章节ID
if in.Id == 0 {
return nil, ecode.Fail.Sub("chapter_id_required")
}
m = m.Where(dao.Chapters.Columns().Id, in.Id)
// 执行查询
if err = m.Scan(out); err != nil {
return nil, ecode.Fail.Sub("chapter_query_failed")
}
// 检查章节是否存在
if out.Id == 0 {
return nil, ecode.NotFound.Sub("chapter_not_found")
}
return out, nil
}
// AppPurchase purchases chapter for app
func (s *sChapter) AppPurchase(ctx context.Context, in *model.ChapterAppPurchaseIn) (out *model.ChapterAppPurchaseOut, err error) {
out = &model.ChapterAppPurchaseOut{}
// 必须指定章节ID
if in.Id == 0 {
return nil, ecode.Fail.Sub("chapter_id_required")
}
// 必须指定用户ID
if in.UserId == 0 {
return nil, ecode.Fail.Sub("user_id_required")
}
// 查询章节信息
chapter := &entity.Chapters{}
err = dao.Chapters.Ctx(ctx).Where(dao.Chapters.Columns().Id, in.Id).Scan(chapter)
if err != nil {
return nil, ecode.Fail.Sub("chapter_query_failed")
}
// 检查章节是否存在
if chapter.Id == 0 {
return nil, ecode.NotFound.Sub("chapter_not_found")
}
// 检查章节是否锁定
if chapter.IsLocked == 0 {
// 章节免费,直接返回成功
out.Success = true
return out, nil
}
// 查询用户信息
user := &entity.Users{}
err = dao.Users.Ctx(ctx).Where(dao.Users.Columns().Id, in.UserId).Scan(user)
if err != nil {
return nil, ecode.Fail.Sub("user_query_failed")
}
// 检查用户是否存在
if user.Id == 0 {
return nil, ecode.NotFound.Sub("user_not_found")
}
// 检查用户积分是否足够
if user.Points < uint64(chapter.RequiredScore) {
return nil, ecode.Fail.Sub("insufficient_points")
}
// 检查是否已经购买过该章节
exist, err := dao.UserChapterPurchases.Ctx(ctx).
Where(dao.UserChapterPurchases.Columns().UserId, in.UserId).
Where(dao.UserChapterPurchases.Columns().ChapterId, in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("purchase_query_failed")
}
if exist {
// 已经购买过,直接返回成功
out.Success = true
return out, nil
}
// 开启事务处理
if err := dao.UserChapterPurchases.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 扣除用户积分
_, err := dao.Users.Ctx(ctx).TX(tx).Where(dao.Users.Columns().Id, in.UserId).Decrement(dao.Users.Columns().Points, chapter.RequiredScore)
if err != nil {
return ecode.Fail.Sub("score_deduction_failed")
}
// 记录购买记录
purchaseId, err := dao.UserChapterPurchases.Ctx(ctx).TX(tx).Data(do.UserChapterPurchases{
UserId: in.UserId,
ChapterId: in.Id,
BookId: chapter.BookId,
PointsUsed: chapter.RequiredScore,
PurchaseTime: gtime.Now(),
}).InsertAndGetId()
if err != nil {
return ecode.Fail.Sub("purchase_record_failed")
}
// 记录用户积分日志
_, err = dao.UserPointsLogs.Ctx(ctx).TX(tx).Data(do.UserPointsLogs{
UserId: in.UserId,
ChangeType: 1, // 消费
PointsChange: -chapter.RequiredScore,
RelatedOrderId: purchaseId,
Description: "购买章节:" + chapter.Title,
CreatedAt: gtime.Now(),
}).Insert()
if err != nil {
return ecode.Fail.Sub("point_log_failed")
}
return nil
}); err != nil {
return nil, err
}
out.Success = true
return out, nil
}
// AppProgress uploads reading progress for app
func (s *sChapter) AppProgress(ctx context.Context, in *model.ChapterAppProgressIn) (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")
}
// 必须指定章节ID
if in.ChapterId == 0 {
return nil, ecode.Fail.Sub("chapter_id_required")
}
// 验证进度范围
if in.Progress < 0 || in.Progress > 100 {
return nil, ecode.Fail.Sub("progress_invalid")
}
// 检查章节是否存在
exist, err := dao.Chapters.Ctx(ctx).Where(dao.Chapters.Columns().Id, in.ChapterId).Exist()
if err != nil {
return nil, ecode.Fail.Sub("chapter_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("chapter_not_found")
}
// 检查用户是否存在
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")
}
// 开启事务处理
if err := dao.UserReadRecords.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 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, in.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, in.ChapterId).
Data(do.UserReadRecords{
Progress: in.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: in.ChapterId,
Progress: in.Progress,
ReadAt: gtime.Now(),
}).Insert()
if err != nil {
return ecode.Fail.Sub("read_record_create_failed")
}
}
// 2. 更新或创建历史记录
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: in.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: in.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).
Where(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
}
}
// 判断是否为最后一章
lastChapter, 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 lastChapter != nil && lastChapter["id"].Int64() == in.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: in.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
}

View File

@ -28,7 +28,7 @@ func (s *sFeedback) List(ctx context.Context, in *model.FeedbackListIn) (out *mo
if in.Status != 0 { if in.Status != 0 {
m = m.Where(dao.Feedbacks.Columns().Status, in.Status) m = m.Where(dao.Feedbacks.Columns().Status, in.Status)
} }
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil { if err = m.Page(in.Page, in.Size).WithAll().ScanAndCount(&out.List, &out.Total, false); err != nil {
return return
} }
return return

View File

@ -6,11 +6,13 @@ package logic
import ( import (
_ "server/internal/logic/admin" _ "server/internal/logic/admin"
_ "server/internal/logic/author"
_ "server/internal/logic/book" _ "server/internal/logic/book"
_ "server/internal/logic/bookshelve"
_ "server/internal/logic/category" _ "server/internal/logic/category"
_ "server/internal/logic/chapter" _ "server/internal/logic/chapter"
_ "server/internal/logic/feedback" _ "server/internal/logic/feedback"
_ "server/internal/logic/read_record"
_ "server/internal/logic/tag"
_ "server/internal/logic/user" _ "server/internal/logic/user"
_ "server/internal/logic/user_follow_author"
_ "server/internal/logic/user_read_record"
) )

View File

@ -1,68 +0,0 @@
package read_record
import (
"context"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/service"
"server/utility/ecode"
)
type sReadRecord struct{}
func New() service.IReadRecord {
return &sReadRecord{}
}
func init() {
service.RegisterReadRecord(New())
}
// List retrieves a paginated list of read records
func (s *sReadRecord) List(ctx context.Context, in *model.ReadRecordListIn) (out *model.ReadRecordListOut, err error) {
out = &model.ReadRecordListOut{}
m := dao.ReadRecords.Ctx(ctx)
if in.UserId != 0 {
m = m.Where(dao.ReadRecords.Columns().UserId, in.UserId)
}
if in.BookId != 0 {
m = m.Where(dao.ReadRecords.Columns().BookId, in.BookId)
}
if in.ChapterId != 0 {
m = m.Where(dao.ReadRecords.Columns().ChapterId, in.ChapterId)
}
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return
}
return
}
// Create adds a new read record
func (s *sReadRecord) Create(ctx context.Context, in *model.ReadRecordAddIn) (out *model.ReadRecordCRUDOut, err error) {
if _, err := dao.ReadRecords.Ctx(ctx).Data(do.ReadRecords{
UserId: in.UserId,
BookId: in.BookId,
ChapterId: in.ChapterId,
}).Insert(); err != nil {
return nil, ecode.Fail.Sub("read_record_create_failed")
}
return &model.ReadRecordCRUDOut{Success: true}, nil
}
// Delete removes a read record by id
func (s *sReadRecord) Delete(ctx context.Context, in *model.ReadRecordDelIn) (out *model.ReadRecordCRUDOut, err error) {
exist, err := dao.ReadRecords.Ctx(ctx).
WherePri(in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("read_record_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("read_record_not_found")
}
_, err = dao.ReadRecords.Ctx(ctx).WherePri(in.Id).Delete()
if err != nil {
return nil, ecode.Fail.Sub("read_record_delete_failed")
}
return &model.ReadRecordCRUDOut{Success: true}, nil
}

View File

@ -1,119 +0,0 @@
package tag
import (
"context"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/service"
"server/utility/ecode"
)
type sTag struct{}
func New() service.ITag {
return &sTag{}
}
func init() {
service.RegisterTag(New())
}
// List retrieves a paginated list of tags
func (s *sTag) List(ctx context.Context, in *model.TagListIn) (out *model.TagListOut, err error) {
out = &model.TagListOut{}
m := dao.Tags.Ctx(ctx)
if in.Name != "" {
m = m.Where(dao.Tags.Columns().Name+" like ?", "%"+in.Name+"%")
}
if in.Type != 0 {
m = m.Where(dao.Tags.Columns().Type, in.Type)
}
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return
}
return
}
func (s *sTag) Create(ctx context.Context, in *model.TagAddIn) (out *model.TagCRUDOut, err error) {
exist, err := dao.Tags.Ctx(ctx).
Where(dao.Tags.Columns().Name, in.Name).
Where(dao.Tags.Columns().Type, in.Type).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("tag_query_failed")
}
if exist {
return nil, ecode.Params.Sub("tag_exists")
}
if _, err := dao.Tags.Ctx(ctx).Data(do.Tags{
Name: in.Name,
Type: in.Type,
}).Insert(); err != nil {
return nil, ecode.Fail.Sub("tag_create_failed")
}
return &model.TagCRUDOut{
Success: true,
}, nil
}
func (s *sTag) Update(ctx context.Context, in *model.TagEditIn) (out *model.TagCRUDOut, err error) {
exist, err := dao.Tags.Ctx(ctx).
WherePri(in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("tag_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("tag_not_found")
}
exist, err = dao.Tags.Ctx(ctx).
Where(dao.Tags.Columns().Name, in.Name).
Where(dao.Tags.Columns().Type, in.Type).
Where("id != ?", in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("tag_query_failed")
}
if exist {
return nil, ecode.Params.Sub("tag_exists")
}
_, err = dao.Tags.Ctx(ctx).
WherePri(in.Id).
Data(do.Tags{
Name: in.Name,
Type: in.Type,
}).Update()
if err != nil {
return nil, ecode.Fail.Sub("tag_update_failed")
}
return &model.TagCRUDOut{
Success: true,
}, nil
}
func (s *sTag) Delete(ctx context.Context, in *model.TagDelIn) (out *model.TagCRUDOut, err error) {
exist, err := dao.Tags.Ctx(ctx).
WherePri(in.Id).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("tag_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("tag_not_found")
}
_, err = dao.Tags.Ctx(ctx).WherePri(in.Id).Delete()
if err != nil {
return nil, ecode.Fail.Sub("tag_delete_failed")
}
return &model.TagCRUDOut{
Success: true,
}, nil
}

View File

@ -11,6 +11,8 @@ import (
"server/utility/encrypt" "server/utility/encrypt"
"server/utility/jwt" "server/utility/jwt"
"strings" "strings"
"github.com/gogf/gf/v2/database/gdb"
) )
type sUser struct{} type sUser struct{}
@ -37,7 +39,13 @@ func (s *sUser) Login(ctx context.Context, in *model.UserLoginIn) (out *model.Us
if !encrypt.ComparePassword(entityUser.PasswordHash, in.Password) { if !encrypt.ComparePassword(entityUser.PasswordHash, in.Password) {
return nil, ecode.Password // 密码不正确 return nil, ecode.Password // 密码不正确
} }
token, err := jwt.GenerateToken(&jwt.TokenIn{UserId: entityUser.Id, Role: "user"}) // 判断是否为作者
author, _ := dao.Authors.Ctx(ctx).Where(do.Authors{UserId: entityUser.Id, Status: 1}).One()
role := "user"
if author != nil && !author.IsEmpty() {
role = "author"
}
token, err := jwt.GenerateToken(&jwt.TokenIn{UserId: entityUser.Id, Role: role})
if err != nil { if err != nil {
return nil, ecode.Fail.Sub("token_generation_failed") return nil, ecode.Fail.Sub("token_generation_failed")
} }
@ -92,7 +100,111 @@ func (s *sUser) Info(ctx context.Context, in *model.UserInfoIn) (out *model.User
} }
func (s *sUser) Delete(ctx context.Context, in *model.UserDeleteIn) (out *model.UserDeleteOut, err error) { func (s *sUser) Delete(ctx context.Context, in *model.UserDeleteIn) (out *model.UserDeleteOut, err error) {
// FIXME // 查询用户信息
user, err := dao.Users.Ctx(ctx).Where(do.Users{Id: in.UserId}).One()
if err != nil {
return nil, ecode.Fail.Sub("database_query_failed")
}
if user == nil {
return nil, ecode.Auth.Sub("user_not_found")
}
var entityUser entity.Users
if err = user.Struct(&entityUser); err != nil {
return nil, ecode.Fail.Sub("data_conversion_failed")
}
// 验证密码
if !encrypt.ComparePassword(entityUser.PasswordHash, in.Password) {
return nil, ecode.Password.Sub("password_incorrect")
}
// 开启事务,删除用户及相关数据
err = dao.Users.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 1. 删除用户阅读记录
_, err := dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("read_record_delete_failed")
}
// 2. 删除用户阅读历史
_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("history_delete_failed")
}
// 3. 删除用户书架
_, err = dao.Bookshelves.Ctx(ctx).TX(tx).
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("bookshelf_delete_failed")
}
// 4. 删除用户关注作者记录
_, err = dao.UserFollowAuthors.Ctx(ctx).TX(tx).
Where(dao.UserFollowAuthors.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("follow_author_delete_failed")
}
// 5. 删除用户评分记录
_, err = dao.BookRatings.Ctx(ctx).TX(tx).
Where(dao.BookRatings.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("rating_delete_failed")
}
// 6. 删除用户章节购买记录
_, err = dao.UserChapterPurchases.Ctx(ctx).TX(tx).
Where(dao.UserChapterPurchases.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("purchase_delete_failed")
}
// 7. 删除用户积分日志
_, err = dao.UserPointsLogs.Ctx(ctx).TX(tx).
Where(dao.UserPointsLogs.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("points_log_delete_failed")
}
// 8. 删除用户反馈
_, err = dao.Feedbacks.Ctx(ctx).TX(tx).
Where(dao.Feedbacks.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("feedback_delete_failed")
}
// 9. 删除作者信息(如果用户是作者)
_, err = dao.Authors.Ctx(ctx).TX(tx).
Where(dao.Authors.Columns().UserId, in.UserId).
Delete()
if err != nil {
return ecode.Fail.Sub("author_delete_failed")
}
// 10. 最后删除用户(软删除)
_, err = dao.Users.Ctx(ctx).TX(tx).Where(do.Users{Id: in.UserId}).Delete()
if err != nil {
return ecode.Fail.Sub("user_delete_failed")
}
return nil
})
if err != nil {
return nil, err
}
return &model.UserDeleteOut{Success: true}, nil return &model.UserDeleteOut{Success: true}, nil
} }

View File

@ -5,11 +5,19 @@ import (
"server/internal/dao" "server/internal/dao"
"server/internal/model" "server/internal/model"
"server/internal/model/do" "server/internal/model/do"
"server/internal/service"
"server/utility/ecode" "server/utility/ecode"
) )
type sUserFollowAuthor struct{} type sUserFollowAuthor struct{}
func New() service.IUserFollowAuthor {
return &sUserFollowAuthor{}
}
func init() {
service.RegisterUserFollowAuthor(New())
}
// List retrieves a paginated list of user follow authors // List retrieves a paginated list of user follow authors
func (s *sUserFollowAuthor) List(ctx context.Context, in *model.UserFollowAuthorListIn) (out *model.UserFollowAuthorListOut, err error) { func (s *sUserFollowAuthor) List(ctx context.Context, in *model.UserFollowAuthorListIn) (out *model.UserFollowAuthorListOut, err error) {
out = &model.UserFollowAuthorListOut{} out = &model.UserFollowAuthorListOut{}
@ -64,3 +72,27 @@ func (s *sUserFollowAuthor) Delete(ctx context.Context, in *model.UserFollowAuth
} }
return &model.UserFollowAuthorCRUDOut{Success: true}, nil return &model.UserFollowAuthorCRUDOut{Success: true}, nil
} }
// Unfollow removes a user follow author by userId and authorId
func (s *sUserFollowAuthor) Unfollow(ctx context.Context, userId int64, authorId int64) (out *model.UserFollowAuthorCRUDOut, err error) {
if userId == 0 || authorId == 0 {
return nil, ecode.Params.Sub("user_id_or_author_id_invalid")
}
// 查找关注记录
var record struct{ Id int64 }
err = dao.UserFollowAuthors.Ctx(ctx).
Where(dao.UserFollowAuthors.Columns().UserId, userId).
Where(dao.UserFollowAuthors.Columns().AuthorId, authorId).
Fields("id").Scan(&record)
if err != nil {
return nil, ecode.Fail.Sub("user_follow_author_query_failed")
}
if record.Id == 0 {
return nil, ecode.NotFound.Sub("user_follow_author_not_found")
}
_, err = dao.UserFollowAuthors.Ctx(ctx).WherePri(record.Id).Delete()
if err != nil {
return nil, ecode.Fail.Sub("user_follow_author_delete_failed")
}
return &model.UserFollowAuthorCRUDOut{Success: true}, nil
}

View File

@ -0,0 +1,97 @@
package user_read_record
import (
"context"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/service"
"server/utility/ecode"
"github.com/gogf/gf/v2/os/gtime"
)
type sUserReadRecord struct{}
func New() service.IUserReadRecord {
return &sUserReadRecord{}
}
func init() {
service.RegisterUserReadRecord(New())
}
// List retrieves a paginated list of user read records
func (s *sUserReadRecord) List(ctx context.Context, in *model.UserReadRecordListIn) (out *model.UserReadRecordListOut, err error) {
out = &model.UserReadRecordListOut{}
m := dao.UserReadRecords.Ctx(ctx)
if in.UserId != 0 {
m = m.Where(dao.UserReadRecords.Columns().UserId, in.UserId)
}
if in.BookId != 0 {
m = m.Where(dao.UserReadRecords.Columns().BookId, in.BookId)
}
if in.ChapterId != 0 {
m = m.Where(dao.UserReadRecords.Columns().ChapterId, in.ChapterId)
}
if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
return
}
return
}
// Create adds a new user read record
func (s *sUserReadRecord) Create(ctx context.Context, in *model.UserReadRecordAddIn) (out *model.UserReadRecordCRUDOut, err error) {
// 检查是否已存在相同的阅读记录
exist, err := dao.UserReadRecords.Ctx(ctx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
Where(dao.UserReadRecords.Columns().ChapterId, in.ChapterId).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("read_record_query_failed")
}
if exist {
// 如果记录已存在,更新进度和时间
_, err = dao.UserReadRecords.Ctx(ctx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
Where(dao.UserReadRecords.Columns().ChapterId, in.ChapterId).
Data(do.UserReadRecords{
Progress: in.Progress,
ReadAt: gtime.Now(),
}).Update()
} else {
// 创建新记录
_, err = dao.UserReadRecords.Ctx(ctx).Data(do.UserReadRecords{
UserId: in.UserId,
BookId: in.BookId,
ChapterId: in.ChapterId,
Progress: in.Progress,
ReadAt: gtime.Now(),
}).Insert()
}
if err != nil {
return nil, ecode.Fail.Sub("read_record_create_failed")
}
return &model.UserReadRecordCRUDOut{Success: true}, nil
}
// Delete removes user read records by userId and bookIds
func (s *sUserReadRecord) Delete(ctx context.Context, in *model.UserReadRecordDelIn) (out *model.UserReadRecordCRUDOut, err error) {
if len(in.BookIds) == 0 {
return nil, ecode.Params.Sub("bookshelve_bookids_empty")
}
// 批量删除指定用户的指定书籍历史记录
_, err = dao.UserReadRecords.Ctx(ctx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
WhereIn(dao.UserReadRecords.Columns().BookId, in.BookIds).
Delete()
if err != nil {
return nil, ecode.Fail.Sub("read_record_delete_failed")
}
return &model.UserReadRecordCRUDOut{Success: true}, nil
}

60
internal/model/author.go Normal file
View File

@ -0,0 +1,60 @@
package model
import "github.com/gogf/gf/v2/frame/g"
type Author struct {
g.Meta `orm:"table:authors"`
Id int64 `json:"id" orm:"id"`
UserId int64 `json:"userId" orm:"user_id"`
PenName string `json:"penName" orm:"pen_name"`
Bio string `json:"bio" orm:"bio"`
}
type AuthorListIn struct {
Page int
Size int
PenName string
Status int
}
type AuthorListOut struct {
Total int
List []Author
}
type AuthorAddIn struct {
UserId int64
PenName string
Bio string
Status int
}
type AuthorEditIn struct {
Id int64
PenName string
Bio string
Status int
}
type AuthorDelIn struct {
Id int64
}
type AuthorCRUDOut struct {
Success bool
}
type AuthorApplyIn struct {
PenName string // 笔名
Bio string // 作者简介
}
type AuthorApplyOut struct {
Success bool
}
type AuthorDetailIn struct {
AuthorId int64
}
type AuthorDetailOut struct {
g.Meta `orm:"table:authors"`
Id int64 `json:"id" orm:"id"`
UserId int64 `json:"userId" orm:"user_id"`
PenName string `json:"penName" orm:"pen_name"`
Bio string `json:"bio" orm:"bio"`
User User `json:"user" orm:"with:id = user_id"`
}

View File

@ -1,23 +1,41 @@
package model package model
import "github.com/gogf/gf/v2/frame/g" import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type Book struct { type Book struct {
g.Meta `orm:"table:books"` g.Meta `orm:"table:books"`
Id int64 `json:"id"` Id int64 `json:"id" orm:"id"`
AuthorId int64 `json:"authorId"` AuthorId int64 `json:"authorId" orm:"author_id"`
CategoryId int64 `json:"categoryId"` Author Author `json:"author" orm:"with:id = author_id"`
Title string `json:"title"` CategoryId int64 `json:"categoryId" orm:"category_id"`
CoverUrl string `json:"coverUrl"` Category Category `json:"category" orm:"with:id = category_id"`
Description string `json:"description"` Title string `json:"title" orm:"title"`
Status int `json:"status"` CoverUrl string `json:"coverUrl" orm:"cover_url"`
WordsCount int `json:"wordsCount"` Description string `json:"description" orm:"description"`
ChaptersCount int `json:"chaptersCount"` Status int `json:"status" orm:"status"`
LatestChapterId int64 `json:"latestChapterId"` WordsCount int `json:"wordsCount" orm:"words_count"`
Rating float64 `json:"rating"` ChaptersCount int `json:"chaptersCount" orm:"chapters_count"`
ReadCount int64 `json:"readCount"` LatestChapterId int64 `json:"latestChapterId" orm:"latest_chapter_id"`
Tags string `json:"tags"` Rating float64 `json:"rating" orm:"rating"`
IsRecommended int `json:"isRecommended"` ReadCount int64 `json:"readCount" orm:"read_count"`
CurrentReaders int64 `json:"currentReaders" orm:"current_readers"`
Tags string `json:"tags" orm:"tags"`
IsRecommended int `json:"isRecommended" orm:"is_recommended"`
IsFeatured int `json:"isFeatured" orm:"is_featured"`
Language string `json:"language" orm:"language"`
}
// App作者信息结构体
type AppAuthor struct {
g.Meta `orm:"table:authors"`
Id int64 `json:"id" orm:"id"`
UserId int64 `json:"-" orm:"user_id"`
PenName string `json:"penName" orm:"pen_name"`
Bio string `json:"bio" orm:"bio"`
Status int `json:"status" orm:"status"`
} }
type BookListIn struct { type BookListIn struct {
@ -28,6 +46,7 @@ type BookListIn struct {
AuthorId int64 AuthorId int64
Status int Status int
IsRecommended int IsRecommended int
Sort string
} }
type BookListOut struct { type BookListOut struct {
Total int Total int
@ -43,6 +62,8 @@ type BookAddIn struct {
Status int Status int
Tags string Tags string
IsRecommended int IsRecommended int
IsFeatured int
Language string
} }
type BookEditIn struct { type BookEditIn struct {
Id int64 Id int64
@ -54,6 +75,8 @@ type BookEditIn struct {
Status int Status int
Tags string Tags string
IsRecommended int IsRecommended int
IsFeatured int
Language string
} }
type BookDelIn struct { type BookDelIn struct {
Id int64 Id int64
@ -62,3 +85,166 @@ type BookDelIn struct {
type BookCRUDOut struct { type BookCRUDOut struct {
Success bool Success bool
} }
type BookHistoryRemoveOut struct {
Success bool `json:"success" dc:"是否成功"`
}
// App 书籍列表项结构体
type BookAppItem struct {
g.Meta `orm:"table:books"`
Id int64 `json:"id" dc:"书籍ID" orm:"id"`
CoverUrl string `json:"coverUrl" dc:"封面图" orm:"cover_url"`
Rating float64 `json:"rating" dc:"评分" orm:"rating"`
Title string `json:"title" dc:"标题" orm:"title"`
Description string `json:"description" dc:"简介" orm:"description"`
AuthorId int64 `json:"authorId" dc:"作者ID" orm:"author_id"`
Author AppAuthor `json:"author" dc:"作者信息" orm:"with:id = author_id"`
IsFeatured int `json:"isFeatured" dc:"是否精选" orm:"is_featured"`
Language string `json:"language" dc:"语言" orm:"language"`
CategoryId int64 `json:"categoryId" dc:"分类ID" orm:"category_id"`
Category Category `json:"category" dc:"分类信息" orm:"with:id = category_id"`
Status int `json:"status" dc:"状态" orm:"status"`
WordsCount int `json:"wordsCount" dc:"字数" orm:"words_count"`
ChaptersCount int `json:"chaptersCount" dc:"章节数" orm:"chapters_count"`
LatestChapterId int64 `json:"latestChapterId" dc:"最新章节ID" orm:"latest_chapter_id"`
ReadCount int64 `json:"readCount" dc:"阅读人数" orm:"read_count"`
CurrentReaders int64 `json:"currentReaders" dc:"在读人数" orm:"current_readers"`
Tags string `json:"tags" dc:"标签" orm:"tags"`
IsRecommended int `json:"isRecommended" dc:"是否推荐" orm:"is_recommended"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间" orm:"created_at"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间" orm:"updated_at"`
HasRated bool `json:"hasRated" dc:"当前用户是否已评分"`
MyRating float64 `json:"myRating" dc:"当前用户评分未评分为0"`
}
// App 书籍列表查询输入参数
type BookAppListIn struct {
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
IsRecommended bool `json:"isRecommended" dc:"是否推荐"`
IsLatest int `json:"isLatest" dc:"是否最新"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
Title string `json:"title" dc:"书名模糊搜索"`
UserId int64 `json:"userId" dc:"用户ID"`
AuthorId int `json:"authorId" dc:"作者ID"`
IsFeatured bool `json:"isFeatured" dc:"是否精选"`
Language string `json:"language" dc:"语言"`
Sort string `json:"sort" dc:"排序字段"`
}
// App 书籍列表输出结构体
type BookAppListOut struct {
Total int `json:"total" dc:"总数"`
List []BookAppItem `json:"list" dc:"书籍列表"`
}
// App 书籍详情查询输入参数
type BookAppDetailIn struct {
Id int64 `json:"id" dc:"书籍ID"`
UserId int64 `json:"userId" dc:"用户ID"`
}
// App 书籍详情输出结构体
type BookAppDetailOut struct {
Id int64 `json:"id" dc:"书籍ID"`
AuthorId int64 `json:"authorId" dc:"作者ID"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
Title string `json:"title" dc:"书名"`
CoverUrl string `json:"coverUrl" dc:"封面图"`
Description string `json:"description" dc:"简介"`
Status int `json:"status" dc:"状态"`
Tags string `json:"tags" dc:"标签"`
IsRecommended int `json:"isRecommended" dc:"是否推荐"`
IsFeatured int `json:"isFeatured" dc:"是否精选"`
Language string `json:"language" dc:"语言"`
Rating float64 `json:"rating" dc:"评分"`
CurrentReaders int64 `json:"currentReaders" dc:"在读人数"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
HasRead bool `json:"hasRead" dc:"是否读过"`
ReadProgress int `json:"readProgress" dc:"阅读进度百分比"`
LastChapterId int64 `json:"lastChapterId" dc:"最近阅读章节ID"`
LastReadAt string `json:"lastReadAt" dc:"最近阅读时间"`
}
// App 用户评分输入参数
type BookAppRateIn struct {
BookId int64 `json:"bookId" dc:"书籍ID"`
UserId int64 `json:"userId" dc:"用户ID"`
Rating float64 `json:"rating" dc:"评分(1-10分)"`
}
// App 用户评分输出结构体
type BookAppRateOut struct {
Success bool `json:"success" dc:"是否成功"`
}
// 书籍评分模型
type BookRating struct {
g.Meta `orm:"table:book_ratings"`
Id int64 `json:"id" orm:"id"`
UserId int64 `json:"userId" orm:"user_id"`
BookId int64 `json:"bookId" orm:"book_id"`
Score float64 `json:"score" orm:"score"`
Comment string `json:"comment" orm:"comment"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at"`
}
// 书籍评分输入参数
type BookRatingAddIn struct {
UserId int64 `json:"userId" dc:"用户ID"`
BookId int64 `json:"bookId" dc:"书籍ID"`
Score float64 `json:"score" dc:"评分(1-10分)"`
Comment string `json:"comment" dc:"用户评论"`
}
// 书籍评分输出结构体
type BookRatingCRUDOut struct {
Success bool `json:"success" dc:"是否成功"`
}
// 我的书籍列表项
// =============================
type MyBookItem struct {
Id int64 `json:"id"`
Title string `json:"title"`
CoverUrl string `json:"coverUrl"`
Description string `json:"description"`
Progress int `json:"progress" dc:"阅读进度百分比"`
IsInShelf bool `json:"isInShelf" dc:"是否在书架"`
LastReadAt string `json:"lastReadAt" dc:"最近阅读时间"`
Status int `json:"status" dc:"书籍状态"`
AuthorId int64 `json:"authorId"`
CategoryId int64 `json:"categoryId"`
}
// 我的书籍列表查询参数
// =============================
type MyBookListIn struct {
Type int // 1-正在读 2-已读完 3-历史记录
Page int
Size int
UserId int64
Sort string
}
// 我的书籍列表返回
// =============================
type MyBookListOut struct {
Total int
List []MyBookItem
}
// =============================
// 新增:单独修改精选状态和推荐状态
// =============================
type BookSetFeaturedIn struct {
Id int64 `json:"id" dc:"书籍ID"`
IsFeatured int `json:"isFeatured" dc:"是否精选"`
}
type BookSetRecommendedIn struct {
Id int64 `json:"id" dc:"书籍ID"`
IsRecommended int `json:"isRecommended" dc:"是否推荐"`
}

View File

@ -0,0 +1,24 @@
package model
import "github.com/gogf/gf/v2/frame/g"
type Bookshelve struct {
g.Meta `orm:"table:bookshelves"`
Id int64 `json:"id" orm:"id"`
UserId int64 `json:"userId" orm:"user_id"`
BookId int64 `json:"bookId" orm:"book_id"`
AddedAt int64 `json:"addedAt" orm:"added_at"`
ReadStatus int `json:"readStatus" orm:"read_status"`
}
type BookshelveAddIn struct {
UserId int64
BookId int64
}
type BookshelveDelIn struct {
UserId int64
BookIds []int64
}
type BookshelveCRUDOut struct {
Success bool
}

View File

@ -4,16 +4,16 @@ import "github.com/gogf/gf/v2/frame/g"
type Category struct { type Category struct {
g.Meta `orm:"table:categories"` g.Meta `orm:"table:categories"`
Id int64 `json:"id" orm:"id"` // 分类ID Id int64 `json:"id" orm:"id"`
Name string `json:"name" orm:"name"` // 分类名称 Name string `json:"name" orm:"name"`
Type int `json:type orm:"type"` // 类型1 男频2 女频 Channel int `json:"channel" orm:"channel"`
} }
type CategoryListIn struct { type CategoryListIn struct {
Page int Page int
Size int Size int
Name string Name string
Type int Channel int
} }
type CategoryListOut struct { type CategoryListOut struct {
Total int Total int
@ -22,15 +22,15 @@ type CategoryListOut struct {
type CategoryAddIn struct { type CategoryAddIn struct {
Name string Name string
Type int // 类型1 男频2 女频 Channel int
} }
type CategoryEditIn struct { type CategoryEditIn struct {
Id int Id int64
Name string Name string
Type int // 类型1 男频2 女频 Channel int
} }
type CategoryDelIn struct { type CategoryDelIn struct {
Id int Id int64
} }
type CategoryCRUDOut struct { type CategoryCRUDOut struct {

View File

@ -1,17 +1,20 @@
package model package model
import "github.com/gogf/gf/v2/frame/g" import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type Chapter struct { type Chapter struct {
g.Meta `orm:"table:chapters"` g.Meta `orm:"table:chapters"`
Id int64 `json:"id"` Id int64 `json:"id" orm:"id"`
BookId int64 `json:"bookId"` BookId int64 `json:"bookId" orm:"book_id"`
Title string `json:"title"` Title string `json:"title" orm:"title"`
Content string `json:"content"` Content string `json:"content" orm:"content"`
WordCount int `json:"wordCount"` WordCount int `json:"wordCount" orm:"word_count"`
Sort int `json:"sort"` Sort int `json:"sort" orm:"sort"`
IsLocked int `json:"isLocked"` IsLocked int `json:"isLocked" orm:"is_locked"`
RequiredScore int `json:"requiredScore"` RequiredScore int `json:"requiredScore" orm:"required_score"`
} }
type ChapterListIn struct { type ChapterListIn struct {
@ -52,3 +55,73 @@ type ChapterDelIn struct {
type ChapterCRUDOut struct { type ChapterCRUDOut struct {
Success bool Success bool
} }
// App 章节列表查询输入参数
type ChapterAppListIn struct {
BookId int64 `json:"bookId" dc:"书籍ID"`
IsDesc bool `json:"isDesc" dc:"是否逆序排列"`
Page int `json:"page" dc:"页码"`
Size int `json:"size" dc:"每页数量"`
UserId int64 `json:"userId" dc:"用户ID"`
}
// App 章节列表输出结构体(不包含 content
type ChapterAppItem struct {
g.Meta `orm:"table:chapters"`
Id int64 `json:"id" orm:"id" dc:"章节ID"`
Title string `json:"title" orm:"title" dc:"章节标题"`
WordCount int `json:"wordCount" orm:"word_count" dc:"字数"`
Sort int `json:"sort" orm:"sort" dc:"排序"`
IsLocked int `json:"isLocked" orm:"is_locked" dc:"是否锁定"`
RequiredScore int `json:"requiredScore" orm:"required_score" dc:"所需积分"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" dc:"更新时间"`
ReadProgress int `json:"readProgress" dc:"阅读进度百分比"`
ReadAt *gtime.Time `json:"readAt" dc:"最后阅读时间"`
}
type ChapterAppListOut struct {
List []ChapterAppItem
Total int
}
// App 章节详情查询输入参数
type ChapterAppDetailIn struct {
Id int64 `json:"id" dc:"章节ID"`
}
// App 章节详情输出结构体
type ChapterAppDetailOut struct {
Id int64 `json:"id" dc:"章节ID"`
BookId int64 `json:"bookId" dc:"书籍ID"`
Title string `json:"title" dc:"章节标题"`
Content string `json:"content" dc:"章节内容"`
WordCount int `json:"wordCount" dc:"字数"`
Sort int `json:"sort" dc:"排序"`
IsLocked int `json:"isLocked" dc:"是否锁定"`
RequiredScore int `json:"requiredScore" dc:"所需积分"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
// App 购买章节输入参数
type ChapterAppPurchaseIn struct {
Id int64 `json:"id" dc:"章节ID"`
UserId int64 `json:"userId" dc:"用户ID"`
}
// App 购买章节输出结构体
type ChapterAppPurchaseOut struct {
Success bool `json:"success" dc:"是否成功"`
}
// App 上传阅读进度输入参数
type ChapterAppProgressIn struct {
BookId int64 `json:"bookId" dc:"书籍ID"`
ChapterId int64 `json:"chapterId" dc:"章节ID"`
Progress int `json:"progress" dc:"阅读进度百分比"`
UserId int64 `json:"userId" dc:"用户ID"`
}
// App 上传阅读进度输出结构体
type ChapterAppProgressOut struct {
Success bool `json:"success" dc:"是否成功"`
}

View File

@ -21,12 +21,14 @@ type Books struct {
Status interface{} // 状态1=连载中2=完结3=下架 Status interface{} // 状态1=连载中2=完结3=下架
WordsCount interface{} // 字数 WordsCount interface{} // 字数
ChaptersCount interface{} // 章节数 ChaptersCount interface{} // 章节数
LatestChapterId interface{} // 最新章节ID
Rating interface{} // 评分0.00~10.00 Rating interface{} // 评分0.00~10.00
ReadCount interface{} // 阅读人数 ReadCount interface{} // 阅读人数
CurrentReaders interface{} // 在读人数
Tags interface{} // 标签(逗号分隔) Tags interface{} // 标签(逗号分隔)
CreatedAt *gtime.Time // 创建时间 CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间 UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳 DeletedAt *gtime.Time // 软删除时间戳
IsRecommended interface{} // 是否推荐0=否1=是 IsRecommended interface{} // 是否推荐0=否1=是
IsFeatured interface{} // 是否精选0=否1=是
Language interface{} // 语言,如 zh=中文en=英文jp=日文
} }

View File

@ -19,4 +19,5 @@ type Bookshelves struct {
LastReadChapterId interface{} // 最后阅读章节ID LastReadChapterId interface{} // 最后阅读章节ID
LastReadPercent interface{} // 阅读进度百分比0.00~100.00 LastReadPercent interface{} // 阅读进度百分比0.00~100.00
LastReadAt *gtime.Time // 最后阅读时间 LastReadAt *gtime.Time // 最后阅读时间
ReadStatus interface{} // 阅读状态1=正在读2=已读完3=已收藏
} }

View File

@ -14,8 +14,8 @@ type Categories struct {
g.Meta `orm:"table:categories, do:true"` g.Meta `orm:"table:categories, do:true"`
Id interface{} // 分类ID Id interface{} // 分类ID
Name interface{} // 分类名称 Name interface{} // 分类名称
Type interface{} // 分类类型1=男频, 2=女频
CreatedAt *gtime.Time // 创建时间 CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间 UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳 DeletedAt *gtime.Time // 软删除时间戳
Channel interface{} // 频道类型1=男频2=女频
} }

View File

@ -14,9 +14,9 @@ type UserPointsLogs struct {
g.Meta `orm:"table:user_points_logs, do:true"` g.Meta `orm:"table:user_points_logs, do:true"`
Id interface{} // 积分流水ID Id interface{} // 积分流水ID
UserId interface{} // 用户ID UserId interface{} // 用户ID
ChangeType interface{} // 变动类型,例如 earn、spend、refund 等 ChangeType interface{} // 变动类型,1=消费(spend), 2=收入(earn)
PointsChange interface{} // 积分变化数,正数增加,负数减少 PointsChange interface{} // 积分变化数,正数增加,负数减少
RelatedOrderId interface{} // 关联订单ID RelatedOrderId interface{} // 关联ID当change_type=1时为chapter_purchases.id当change_type=2时为advertisement_records.id
Description interface{} // 变动说明 Description interface{} // 变动说明
CreatedAt *gtime.Time // 变动时间 CreatedAt *gtime.Time // 变动时间
} }

View File

@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// UserReadHistory is the golang structure of table user_read_history for DAO operations like Where/Data.
type UserReadHistory struct {
g.Meta `orm:"table:user_read_history, do:true"`
Id interface{} // 历史记录ID
UserId interface{} // 用户ID
BookId interface{} // 小说ID
ChapterId interface{} // 最后阅读章节ID
ReadAt *gtime.Time // 最后阅读时间
}

View File

@ -9,12 +9,15 @@ import (
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
) )
// ReadRecords is the golang structure of table read_records for DAO operations like Where/Data. // UserReadRecords is the golang structure of table user_read_records for DAO operations like Where/Data.
type ReadRecords struct { type UserReadRecords struct {
g.Meta `orm:"table:read_records, do:true"` g.Meta `orm:"table:user_read_records, do:true"`
Id interface{} // 记录ID Id interface{} // 记录ID
UserId interface{} // 用户ID UserId interface{} // 用户ID
BookId interface{} // 小说ID BookId interface{} // 小说ID
ChapterId interface{} // 章节ID ChapterId interface{} // 章节ID
Progress interface{} // 阅读进度百分比(0-100)
ReadAt *gtime.Time // 阅读时间 ReadAt *gtime.Time // 阅读时间
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
} }

View File

@ -19,12 +19,14 @@ type Books struct {
Status int `json:"status" orm:"status" description:"状态1=连载中2=完结3=下架"` // 状态1=连载中2=完结3=下架 Status int `json:"status" orm:"status" description:"状态1=连载中2=完结3=下架"` // 状态1=连载中2=完结3=下架
WordsCount int `json:"wordsCount" orm:"words_count" description:"字数"` // 字数 WordsCount int `json:"wordsCount" orm:"words_count" description:"字数"` // 字数
ChaptersCount int `json:"chaptersCount" orm:"chapters_count" description:"章节数"` // 章节数 ChaptersCount int `json:"chaptersCount" orm:"chapters_count" description:"章节数"` // 章节数
LatestChapterId int64 `json:"latestChapterId" orm:"latest_chapter_id" description:"最新章节ID"` // 最新章节ID
Rating float64 `json:"rating" orm:"rating" description:"评分0.00~10.00"` // 评分0.00~10.00 Rating float64 `json:"rating" orm:"rating" description:"评分0.00~10.00"` // 评分0.00~10.00
ReadCount int64 `json:"readCount" orm:"read_count" description:"阅读人数"` // 阅读人数 ReadCount int64 `json:"readCount" orm:"read_count" description:"阅读人数"` // 阅读人数
CurrentReaders int64 `json:"currentReaders" orm:"current_readers" description:"在读人数"` // 在读人数
Tags string `json:"tags" orm:"tags" description:"标签(逗号分隔)"` // 标签(逗号分隔) Tags string `json:"tags" orm:"tags" description:"标签(逗号分隔)"` // 标签(逗号分隔)
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳 DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
IsRecommended int `json:"isRecommended" orm:"is_recommended" description:"是否推荐0=否1=是"` // 是否推荐0=否1=是 IsRecommended int `json:"isRecommended" orm:"is_recommended" description:"是否推荐0=否1=是"` // 是否推荐0=否1=是
IsFeatured int `json:"isFeatured" orm:"is_featured" description:"是否精选0=否1=是"` // 是否精选0=否1=是
Language string `json:"language" orm:"language" description:"语言,如 zh=中文en=英文jp=日文"` // 语言,如 zh=中文en=英文jp=日文
} }

View File

@ -17,4 +17,5 @@ type Bookshelves struct {
LastReadChapterId int64 `json:"lastReadChapterId" orm:"last_read_chapter_id" description:"最后阅读章节ID"` // 最后阅读章节ID LastReadChapterId int64 `json:"lastReadChapterId" orm:"last_read_chapter_id" description:"最后阅读章节ID"` // 最后阅读章节ID
LastReadPercent float64 `json:"lastReadPercent" orm:"last_read_percent" description:"阅读进度百分比0.00~100.00"` // 阅读进度百分比0.00~100.00 LastReadPercent float64 `json:"lastReadPercent" orm:"last_read_percent" description:"阅读进度百分比0.00~100.00"` // 阅读进度百分比0.00~100.00
LastReadAt *gtime.Time `json:"lastReadAt" orm:"last_read_at" description:"最后阅读时间"` // 最后阅读时间 LastReadAt *gtime.Time `json:"lastReadAt" orm:"last_read_at" description:"最后阅读时间"` // 最后阅读时间
ReadStatus int `json:"readStatus" orm:"read_status" description:"阅读状态1=正在读2=已读完3=已收藏"` // 阅读状态1=正在读2=已读完3=已收藏
} }

View File

@ -12,8 +12,8 @@ import (
type Categories struct { type Categories struct {
Id int64 `json:"id" orm:"id" description:"分类ID"` // 分类ID Id int64 `json:"id" orm:"id" description:"分类ID"` // 分类ID
Name string `json:"name" orm:"name" description:"分类名称"` // 分类名称 Name string `json:"name" orm:"name" description:"分类名称"` // 分类名称
Type int `json:"type" orm:"type" description:"分类类型1=男频, 2=女频"` // 分类类型1=男频, 2=女频
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳 DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
Channel int `json:"channel" orm:"channel" description:"频道类型1=男频2=女频"` // 频道类型1=男频2=女频
} }

View File

@ -12,9 +12,9 @@ import (
type UserPointsLogs struct { type UserPointsLogs struct {
Id int64 `json:"id" orm:"id" description:"积分流水ID"` // 积分流水ID Id int64 `json:"id" orm:"id" description:"积分流水ID"` // 积分流水ID
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID
ChangeType string `json:"changeType" orm:"change_type" description:"变动类型,例如 earn、spend、refund 等"` // 变动类型,例如 earn、spend、refund 等 ChangeType int `json:"changeType" orm:"change_type" description:"变动类型,1=消费(spend), 2=收入(earn)"` // 变动类型1=消费(spend), 2=收入(earn)
PointsChange int `json:"pointsChange" orm:"points_change" description:"积分变化数,正数增加,负数减少"` // 积分变化数,正数增加,负数减少 PointsChange int `json:"pointsChange" orm:"points_change" description:"积分变化数,正数增加,负数减少"` // 积分变化数,正数增加,负数减少
RelatedOrderId int64 `json:"relatedOrderId" orm:"related_order_id" description:"关联订单ID"` // 关联订单ID RelatedOrderId int64 `json:"relatedOrderId" orm:"related_order_id" description:"关联ID当change_type=1时为chapter_purchases.id当change_type=2时为advertisement_records.id"` // 关联ID当change_type=1时为chapter_purchases.id当change_type=2时为advertisement_records.id
Description string `json:"description" orm:"description" description:"变动说明"` // 变动说明 Description string `json:"description" orm:"description" description:"变动说明"` // 变动说明
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"变动时间"` // 变动时间 CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"变动时间"` // 变动时间
} }

View File

@ -8,11 +8,11 @@ import (
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
) )
// ReadRecords is the golang structure for table read_records. // UserReadHistory is the golang structure for table user_read_history.
type ReadRecords struct { type UserReadHistory struct {
Id int64 `json:"id" orm:"id" description:"记录ID"` // 记录ID Id int64 `json:"id" orm:"id" description:"历史记录ID"` // 历史记录ID
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID
BookId int64 `json:"bookId" orm:"book_id" description:"小说ID"` // 小说ID BookId int64 `json:"bookId" orm:"book_id" description:"小说ID"` // 小说ID
ChapterId int64 `json:"chapterId" orm:"chapter_id" description:"章节ID"` // 章节ID ChapterId int64 `json:"chapterId" orm:"chapter_id" description:"最后阅读章节ID"` // 最后阅读章节ID
ReadAt *gtime.Time `json:"readAt" orm:"read_at" description:"阅读时间"` // 阅读时间 ReadAt *gtime.Time `json:"readAt" orm:"read_at" description:"最后阅读时间"` // 最后阅读时间
} }

View File

@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// UserReadRecords is the golang structure for table user_read_records.
type UserReadRecords struct {
Id int64 `json:"id" orm:"id" description:"记录ID"` // 记录ID
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID
BookId int64 `json:"bookId" orm:"book_id" description:"小说ID"` // 小说ID
ChapterId int64 `json:"chapterId" orm:"chapter_id" description:"章节ID"` // 章节ID
Progress int `json:"progress" orm:"progress" description:"阅读进度百分比(0-100)"` // 阅读进度百分比(0-100)
ReadAt *gtime.Time `json:"readAt" orm:"read_at" description:"阅读时间"` // 阅读时间
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
}

View File

@ -1,13 +1,18 @@
package model package model
import "github.com/gogf/gf/v2/frame/g" import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type Feedback struct { type Feedback struct {
g.Meta `orm:"table:feedbacks"` g.Meta `orm:"table:feedbacks"`
Id int64 `json:"id"` Id int64 `json:"id" orm:"id"`
UserId int64 `json:"userId"` UserId int64 `json:"userId" orm:"user_id"`
Content string `json:"content"` Content string `json:"content" orm:"content"`
Status int `json:"status"` Status int `json:"status" orm:"status"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at"`
User User `json:"user" orm:"with:id=user_id"`
} }
type FeedbackListIn struct { type FeedbackListIn struct {

View File

@ -1,36 +0,0 @@
package model
import "github.com/gogf/gf/v2/frame/g"
type ReadRecord struct {
g.Meta `orm:"table:read_records"`
Id int64 `json:"id"`
UserId int64 `json:"userId"`
BookId int64 `json:"bookId"`
ChapterId int64 `json:"chapterId"`
ReadAt int64 `json:"readAt"`
}
type ReadRecordListIn struct {
Page int
Size int
UserId int64
BookId int64
ChapterId int64
}
type ReadRecordListOut struct {
Total int
List []ReadRecord
}
type ReadRecordAddIn struct {
UserId int64
BookId int64
ChapterId int64
}
type ReadRecordDelIn struct {
Id int64
}
type ReadRecordCRUDOut struct {
Success bool
}

View File

@ -4,9 +4,9 @@ import "github.com/gogf/gf/v2/frame/g"
type Tag struct { type Tag struct {
g.Meta `orm:"table:tags"` g.Meta `orm:"table:tags"`
Id int64 `json:"id"` Id int64 `json:"id" orm:"id"`
Name string `json:"name"` Name string `json:"name" orm:"name"`
Type int `json:"type"` // 1=主题, 2=角色, 3=情节 Type int `json:"type" orm:"type"` // 1=主题, 2=角色, 3=情节
} }
type TagListIn struct { type TagListIn struct {

Some files were not shown because too many files have changed in this diff Show More