feat: Enhance Swagger documentation with new admin and user endpoints
Some checks failed
build quyun / Build (push) Failing after 2m1s

- Added definitions for admin authentication, media, posts, orders, and user management.
- Implemented new API paths for admin functionalities including authentication, media management, order processing, and user statistics.
- Updated existing endpoints to improve clarity and structure in the Swagger documentation.
- Introduced new response schemas for various operations to standardize API responses.
This commit is contained in:
2025-12-19 23:43:46 +08:00
parent 49072ddd79
commit 7b18a6a0e6
14 changed files with 4180 additions and 120 deletions

View File

@@ -22,8 +22,14 @@ type TokenResponse struct {
// Login
//
// @Router /admin/auth [post]
// @Bind body body
// @Summary 管理员登录
// @Tags Admin Auth
// @Accept json
// @Produce json
// @Param body body AuthBody true "请求体"
// @Success 200 {object} TokenResponse "成功"
// @Router /admin/auth [post]
// @Bind body body
func (ctl *auth) Login(ctx fiber.Ctx, body *AuthBody) (*TokenResponse, error) {
if body.Username == "pl.yang" && body.Password == "Xixi@0202" {
claim := ctl.jwt.CreateClaims(jwt.BaseClaims{

View File

@@ -17,17 +17,27 @@ type medias struct {
// List medias
//
// @Router /admin/medias [get]
// @Bind pagination query
// @Bind query query
// @Summary 媒体列表
// @Tags Admin Medias
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query ListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=models.Media} "成功"
// @Router /admin/medias [get]
// @Bind pagination query
// @Bind query query
func (ctl *medias) List(ctx fiber.Ctx, pagination *requests.Pagination, query *ListQuery) (*requests.Pager, error) {
return services.Medias.List(ctx, pagination, models.MediaQuery.Name.Like(database.WrapLike(*query.Keyword)))
}
// Show media
//
// @Router /admin/medias/:id [get]
// @Bind id path
// @Summary 媒体预览(跳转到签名 URL
// @Tags Admin Medias
// @Param id path int64 true "媒体 ID"
// @Success 302 {string} string "跳转"
// @Router /admin/medias/:id [get]
// @Bind id path
func (ctl *medias) Show(ctx fiber.Ctx, id int64) error {
media, err := services.Medias.FindByID(ctx, id)
if err != nil {
@@ -44,8 +54,13 @@ func (ctl *medias) Show(ctx fiber.Ctx, id int64) error {
// Delete
//
// @Router /admin/medias/:id [delete]
// @Bind id path
// @Summary 删除媒体
// @Tags Admin Medias
// @Produce json
// @Param id path int64 true "媒体 ID"
// @Success 204 {object} any "成功"
// @Router /admin/medias/:id [delete]
// @Bind id path
func (ctl *medias) Delete(ctx fiber.Ctx, id int64) error {
media, err := services.Medias.FindByID(ctx, id)
if err != nil {

View File

@@ -26,9 +26,15 @@ type orders struct {
// List users
//
// @Router /admin/orders [get]
// @Bind pagination query
// @Bind query query
// @Summary 订单列表
// @Tags Admin Orders
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query OrderListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=services.OrderListItem} "成功"
// @Router /admin/orders [get]
// @Bind pagination query
// @Bind query query
func (ctl *orders) List(
ctx fiber.Ctx,
pagination *requests.Pagination,
@@ -47,8 +53,14 @@ func (ctl *orders) List(
}
// Refund
// @Router /admin/orders/:id/refund [post]
// @Bind id path
//
// @Summary 订单退款
// @Tags Admin Orders
// @Produce json
// @Param id path int64 true "订单 ID"
// @Success 200 {object} any "成功"
// @Router /admin/orders/:id/refund [post]
// @Bind id path
func (ctl *orders) Refund(ctx fiber.Ctx, id int64) error {
order, err := services.Orders.FindByID(ctx, id)
if err != nil {

View File

@@ -21,9 +21,15 @@ type posts struct{}
// List posts
//
// @Router /admin/posts [get]
// @Bind pagination query
// @Bind query query
// @Summary 作品列表
// @Tags Admin Posts
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query ListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=PostItem} "成功"
// @Router /admin/posts [get]
// @Bind pagination query
// @Bind query query
func (ctl *posts) List(ctx fiber.Ctx, pagination *requests.Pagination, query *ListQuery) (*requests.Pager, error) {
conds := []gen.Condition{
models.PostQuery.Title.Like(*query.Keyword),
@@ -69,8 +75,14 @@ type PostForm struct {
// Create
//
// @Router /admin/posts [post]
// @Bind form body
// @Summary 创建作品
// @Tags Admin Posts
// @Accept json
// @Produce json
// @Param form body PostForm true "请求体"
// @Success 200 {object} any "成功"
// @Router /admin/posts [post]
// @Bind form body
func (ctl *posts) Create(ctx fiber.Ctx, form *PostForm) error {
post := models.Post{
Title: form.Title,
@@ -107,9 +119,16 @@ func (ctl *posts) Create(ctx fiber.Ctx, form *PostForm) error {
// Update posts
//
// @Router /admin/posts/:id [put]
// @Bind id path
// @Bind form body
// @Summary 更新作品
// @Tags Admin Posts
// @Accept json
// @Produce json
// @Param id path int64 true "作品 ID"
// @Param form body PostForm true "请求体"
// @Success 200 {object} any "成功"
// @Router /admin/posts/:id [put]
// @Bind id path
// @Bind form body
func (ctl *posts) Update(ctx fiber.Ctx, id int64, form *PostForm) error {
post, err := services.Posts.FindByID(ctx, id)
if err != nil {
@@ -147,8 +166,13 @@ func (ctl *posts) Update(ctx fiber.Ctx, id int64, form *PostForm) error {
// Delete posts
//
// @Router /admin/posts/:id [delete]
// @Bind id path
// @Summary 删除作品
// @Tags Admin Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Success 204 {object} any "成功"
// @Router /admin/posts/:id [delete]
// @Bind id path
func (ctl *posts) Delete(ctx fiber.Ctx, id int64) error {
post, err := services.Posts.FindByID(ctx, id)
if err != nil {
@@ -172,8 +196,13 @@ type PostItem struct {
// Show posts by id
//
// @Router /admin/posts/:id [get]
// @Bind id path
// @Summary 作品详情
// @Tags Admin Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Success 200 {object} PostItem "成功"
// @Router /admin/posts/:id [get]
// @Bind id path
func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) {
post, err := services.Posts.FindByID(ctx, id)
if err != nil {
@@ -194,9 +223,15 @@ func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) {
// SendTo
//
// @Router /admin/posts/:id/send-to/:userId [post]
// @Bind id path
// @Bind userId path
// @Summary 赠送作品给用户
// @Tags Admin Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Param userId path int64 true "用户 ID"
// @Success 200 {object} any "成功"
// @Router /admin/posts/:id/send-to/:userId [post]
// @Bind id path
// @Bind userId path
func (ctl *posts) SendTo(ctx fiber.Ctx, id, userId int64) error {
post, err := services.Posts.FindByID(ctx, id)
if err != nil {

View File

@@ -22,7 +22,11 @@ type StatisticsResponse struct {
// dashboard statistics
//
// @Router /admin/statistics [get]
// @Summary 仪表盘统计
// @Tags Admin Statistics
// @Produce json
// @Success 200 {object} StatisticsResponse "成功"
// @Router /admin/statistics [get]
func (s *statistics) statistics(ctx fiber.Ctx) (*StatisticsResponse, error) {
statistics := &StatisticsResponse{}

View File

@@ -34,10 +34,17 @@ type PreCheckResp struct {
// PreUploadCheck
//
// @Router /admin/uploads/pre-uploaded-check/:md5.:ext [get]
// @Bind md5 path
// @Bind ext path
// @Bind mime query
// @Summary 预上传检查
// @Tags Admin Uploads
// @Produce json
// @Param md5 path string true "文件 MD5"
// @Param ext path string true "文件扩展名(不含点)"
// @Param mime query string true "文件 MIME 类型"
// @Success 200 {object} PreCheckResp "成功"
// @Router /admin/uploads/pre-uploaded-check/:md5.:ext [get]
// @Bind md5 path
// @Bind ext path
// @Bind mime query
func (up *uploads) PreUploadCheck(ctx fiber.Ctx, md5, ext, mime string) (*PreCheckResp, error) {
_, err := services.Medias.GetByHash(ctx, md5)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
@@ -61,8 +68,14 @@ type PostUploadedForm struct {
// PostUploadedAction
//
// @Router /admin/uploads/post-uploaded-action [post]
// @Bind body body
// @Summary 上传完成回调处理
// @Tags Admin Uploads
// @Accept json
// @Produce json
// @Param body body PostUploadedForm true "请求体"
// @Success 200 {object} any "成功"
// @Router /admin/uploads/post-uploaded-action [post]
// @Bind body body
func (up *uploads) PostUploadedAction(ctx fiber.Ctx, body *PostUploadedForm) error {
m, err := services.Medias.GetByHash(ctx, body.Md5)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {

View File

@@ -19,9 +19,15 @@ type users struct{}
// List users
//
// @Router /admin/users [get]
// @Bind pagination query
// @Bind query query
// @Summary 用户列表
// @Tags Admin Users
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query UserListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=models.User} "成功"
// @Router /admin/users [get]
// @Bind pagination query
// @Bind query query
func (ctl *users) List(ctx fiber.Ctx, pagination *requests.Pagination, query *UserListQuery) (*requests.Pager, error) {
conds := []gen.Condition{
models.UserQuery.Username.Like(database.WrapLike(*query.Keyword)),
@@ -31,17 +37,28 @@ func (ctl *users) List(ctx fiber.Ctx, pagination *requests.Pagination, query *Us
// Show user
//
// @Router /admin/users/:id [get]
// @Bind id path
// @Summary 用户详情
// @Tags Admin Users
// @Produce json
// @Param id path int64 true "用户 ID"
// @Success 200 {object} models.User "成功"
// @Router /admin/users/:id [get]
// @Bind id path
func (ctl *users) Show(ctx fiber.Ctx, id int64) (*models.User, error) {
return services.Users.FindByID(ctx, id)
}
// Articles show user bought articles
//
// @Router /admin/users/:id/articles [get]
// @Bind id path
// @Bind pagination query
// @Summary 用户已购作品
// @Tags Admin Users
// @Produce json
// @Param id path int64 true "用户 ID"
// @Param pagination query requests.Pagination false "分页参数"
// @Success 200 {object} requests.Pager{items=models.Post} "成功"
// @Router /admin/users/:id/articles [get]
// @Bind id path
// @Bind pagination query
func (ctl *users) Articles(ctx fiber.Ctx, id int64, pagination *requests.Pagination) (*requests.Pager, error) {
return services.Posts.Bought(ctx, id, pagination)
}
@@ -52,9 +69,16 @@ type UserBalance struct {
// Balance
//
// @Router /admin/users/:id/balance [post]
// @Bind id path
// @Bind balance body
// @Summary 调整用户余额
// @Tags Admin Users
// @Accept json
// @Produce json
// @Param id path int64 true "用户 ID"
// @Param balance body UserBalance true "请求体"
// @Success 200 {object} any "成功"
// @Router /admin/users/:id/balance [post]
// @Bind id path
// @Bind balance body
func (ctl *users) Balance(ctx fiber.Ctx, id int64, balance *UserBalance) error {
user, err := services.Users.FindByID(ctx, id)
if err != nil {

View File

@@ -38,10 +38,16 @@ type posts struct {
// List posts
//
// @Router /posts [get]
// @Bind pagination query
// @Bind query query
// @Bind user local
// @Summary 作品列表
// @Tags Posts
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query ListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=PostItem} "成功"
// @Router /posts [get]
// @Bind pagination query
// @Bind query query
// @Bind user local
func (ctl *posts) List(
ctx fiber.Ctx,
pagination *requests.Pagination,
@@ -123,9 +129,14 @@ type PostItem struct {
// Show
//
// @Router /posts/:id/show [get]
// @Bind id path
// @Bind user local
// @Summary 作品详情
// @Tags Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Success 200 {object} PostItem "成功"
// @Router /posts/:id/show [get]
// @Bind id path
// @Bind user local
func (ctl *posts) Show(ctx fiber.Ctx, id int64, user *models.User) (*PostItem, error) {
log.Infof("Fetching post with ID: %d", id)
@@ -179,9 +190,14 @@ type PlayUrl struct {
// Play
//
// @Router /posts/:id/play [get]
// @Bind id path
// @Bind user local
// @Summary 获取播放地址
// @Tags Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Success 200 {object} PlayUrl "成功"
// @Router /posts/:id/play [get]
// @Bind id path
// @Bind user local
func (ctl *posts) Play(ctx fiber.Ctx, id int64, user *models.User) (*PlayUrl, error) {
log := log.WithField("PlayPostID", strconv.FormatInt(id, 10))
// return &PlayUrl{
@@ -230,10 +246,16 @@ func (ctl *posts) Play(ctx fiber.Ctx, id int64, user *models.User) (*PlayUrl, er
// Mine posts
//
// @Router /posts/mine [get]
// @Bind pagination query
// @Bind query query
// @Bind user local
// @Summary 我的已购作品
// @Tags Posts
// @Produce json
// @Param pagination query requests.Pagination false "分页参数"
// @Param query query ListQuery false "筛选条件"
// @Success 200 {object} requests.Pager{items=PostItem} "成功"
// @Router /posts/mine [get]
// @Bind pagination query
// @Bind query query
// @Bind user local
func (ctl *posts) Mine(
ctx fiber.Ctx,
pagination *requests.Pagination,
@@ -292,9 +314,14 @@ func (ctl *posts) Mine(
// Buy
//
// @Router /posts/:id/buy [post]
// @Bind id path
// @Bind user local
// @Summary 购买作品
// @Tags Posts
// @Produce json
// @Param id path int64 true "作品 ID"
// @Success 200 {object} wechat.JSAPIPayParams "成功(余额支付返回 AppId=balance"
// @Router /posts/:id/buy [post]
// @Bind id path
// @Bind user local
func (ctl *posts) Buy(ctx fiber.Ctx, id int64, user *models.User) (*wechat.JSAPIPayParams, error) {
bought, err := services.Users.HasBought(ctx, user.ID, id)
if err != nil {

View File

@@ -21,8 +21,14 @@ type UserInfo struct {
Balance int64 `json:"balance"`
}
// @Router /users/profile [get]
// @Bind user local
// Profile 获取当前登录用户的基础信息。
//
// @Summary 获取个人信息
// @Tags Users
// @Produce json
// @Success 200 {object} UserInfo "成功"
// @Router /users/profile [get]
// @Bind user local
func (ctl *users) Profile(ctx fiber.Ctx, user *models.User) (*UserInfo, error) {
return &UserInfo{
ID: user.ID,
@@ -37,11 +43,17 @@ type ProfileForm struct {
Username string `json:"username" validate:"required"`
}
// Update
// Update 修改当前登录用户的用户名。
//
// @Router /users/username [put]
// @Bind user local
// @Bind form body
// @Summary 修改用户名
// @Tags Users
// @Accept json
// @Produce json
// @Param form body ProfileForm true "请求体"
// @Success 200 {object} any "成功"
// @Router /users/username [put]
// @Bind user local
// @Bind form body
func (ctl *users) Update(ctx fiber.Ctx, user *models.User, form *ProfileForm) error {
username := strings.TrimSpace(form.Username)
if len([]rune(username)) > 12 {

View File

@@ -30,18 +30,20 @@ type Filter struct {
type ResponseItem struct{}
// Foo
// Foo 演示端点:展示 Fiber 参数绑定与 Swagger 注释写法。
//
// @Summary Test
// @Description Test
// @Tags Test
// @Accept json
// @Summary 演示接口
// @Tags Demo
// @Accept multipart/form-data
// @Produce json
//
// @Param id path int true "ID"
// @Param query query Filter true "Filter"
// @Param pager query requests.Pagination true "Pager"
// @Success 200 {object} requests.Pager{list=ResponseItem} "成功"
// @Param id path int true "ID"
// @Param pager query requests.Pagination false "分页参数"
// @Param query query FooQuery false "查询参数"
// @Param Content-Type header string false "内容类型"
// @Param folder formData string false "上传到指定文件夹"
// @Param file formData file true "上传文件"
// @Success 200 {object} requests.Pager{items=ResponseItem} "成功"
//
// @Router /v1/medias/:id [post]
// @Bind query query

View File

@@ -14,6 +14,13 @@ import (
"go.ipao.vip/gen/types"
)
// OrderListItem 用于订单列表展示,补充作品标题与用户名等冗余信息,避免前端二次查询。
type OrderListItem struct {
*models.Order
PostTitle string `json:"post_title"`
Username string `json:"username"`
}
// @provider
type orders struct{}
@@ -51,15 +58,8 @@ func (m *orders) List(
postMap := lo.SliceToMap(posts, func(p *models.Post) (int64, *models.Post) { return p.ID, p })
userMap := lo.SliceToMap(users, func(u *models.User) (int64, *models.User) { return u.ID, u })
// OrderListItem 用于订单列表展示,补充作品标题与用户名等冗余信息,避免前端二次查询。
type orderListItem struct {
*models.Order
PostTitle string `json:"post_title"`
Username string `json:"username"`
}
items := lo.Map(orders, func(o *models.Order, _ int) *orderListItem {
item := &orderListItem{Order: o}
items := lo.Map(orders, func(o *models.Order, _ int) *OrderListItem {
item := &OrderListItem{Order: o}
if post, ok := postMap[o.PostID]; ok {
item.PostTitle = post.Title
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff