From b8c2c245f203e0826bbb82e4640bcb113c20fb3b Mon Sep 17 00:00:00 2001 From: yanghao05 Date: Fri, 25 Apr 2025 11:39:28 +0800 Subject: [PATCH] feat: update post detail page --- backend/app/http/admin/auth.go | 2 +- backend/app/http/admin/medias.go | 6 +- backend/app/http/admin/orders.go | 2 +- backend/app/http/admin/posts.go | 12 +-- backend/app/http/admin/routes.gen.go | 32 +++---- backend/app/http/admin/uploads.go | 4 +- backend/app/http/admin/users.go | 6 +- backend/app/http/posts.go | 97 ++++++++++++++++----- backend/app/http/provider.gen.go | 3 + backend/app/http/routes.gen.go | 4 +- backend/app/middlewares/mid_auth.go | 7 ++ backend/app/models/posts.go | 25 ++++++ frontend/wechat/src/api/client.js | 2 +- frontend/wechat/src/api/post_detail.json | 17 ++++ frontend/wechat/src/views/ArticleDetail.vue | 48 ++++++---- 15 files changed, 191 insertions(+), 76 deletions(-) create mode 100644 frontend/wechat/src/api/post_detail.json diff --git a/backend/app/http/admin/auth.go b/backend/app/http/admin/auth.go index 7f3ca04..69f6980 100644 --- a/backend/app/http/admin/auth.go +++ b/backend/app/http/admin/auth.go @@ -21,7 +21,7 @@ type TokenResponse struct { } // Login -// @Router /v1/admin/auth [post] +// @Router /admin/auth [post] // @Bind body body func (ctl *auth) Login(ctx fiber.Ctx, body *AuthBody) (*TokenResponse, error) { if body.Username == "admin" && body.Password == "xixi@0202" { diff --git a/backend/app/http/admin/medias.go b/backend/app/http/admin/medias.go index 2c68498..a72b12b 100644 --- a/backend/app/http/admin/medias.go +++ b/backend/app/http/admin/medias.go @@ -14,7 +14,7 @@ type medias struct { } // List medias -// @Router /v1/admin/medias [get] +// @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) { @@ -23,7 +23,7 @@ func (ctl *medias) List(ctx fiber.Ctx, pagination *requests.Pagination, query *L } // Show media -// @Router /v1/admin/medias/:id [get] +// @Router /admin/medias/:id [get] // @Bind id path func (ctl *medias) Show(ctx fiber.Ctx, id int64) error { media, err := models.Medias.GetByID(ctx.Context(), id) @@ -40,7 +40,7 @@ func (ctl *medias) Show(ctx fiber.Ctx, id int64) error { } // Delete -// @Router /v1/admin/medias/:id [delete] +// @Router /admin/medias/:id [delete] // @Bind id path func (ctl *medias) Delete(ctx fiber.Ctx, id int64) error { media, err := models.Medias.GetByID(ctx.Context(), id) diff --git a/backend/app/http/admin/orders.go b/backend/app/http/admin/orders.go index f82115e..c3aca74 100644 --- a/backend/app/http/admin/orders.go +++ b/backend/app/http/admin/orders.go @@ -16,7 +16,7 @@ type OrderListQuery struct { type orders struct{} // List users -// @Router /v1/admin/orders [get] +// @Router /admin/orders [get] // @Bind pagination query // @Bind query query func (ctl *orders) List(ctx fiber.Ctx, pagination *requests.Pagination, query *OrderListQuery) (*requests.Pager, error) { diff --git a/backend/app/http/admin/posts.go b/backend/app/http/admin/posts.go index 4d23f3e..d1c389f 100644 --- a/backend/app/http/admin/posts.go +++ b/backend/app/http/admin/posts.go @@ -18,7 +18,7 @@ type ListQuery struct { type posts struct{} // List posts -// @Router /v1/admin/posts [get] +// @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) { @@ -63,7 +63,7 @@ type PostForm struct { } // Create -// @Router /v1/admin/posts [post] +// @Router /admin/posts [post] // @Bind form body func (ctl *posts) Create(ctx fiber.Ctx, form *PostForm) error { post := model.Posts{ @@ -100,7 +100,7 @@ func (ctl *posts) Create(ctx fiber.Ctx, form *PostForm) error { } // Update posts -// @Router /v1/admin/posts/:id [put] +// @Router /admin/posts/:id [put] // @Bind id path // @Bind form body func (ctl *posts) Update(ctx fiber.Ctx, id int64, form *PostForm) error { @@ -148,7 +148,7 @@ func (ctl *posts) Update(ctx fiber.Ctx, id int64, form *PostForm) error { } // Delete posts -// @Router /v1/admin/posts/:id [delete] +// @Router /admin/posts/:id [delete] // @Bind id path func (ctl *posts) Delete(ctx fiber.Ctx, id int64) error { post, err := models.Posts.GetByID(ctx.Context(), id) @@ -172,7 +172,7 @@ type PostItem struct { } // Show posts by id -// @Router /v1/admin/posts/:id [get] +// @Router /admin/posts/:id [get] // @Bind id path func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) { post, err := models.Posts.GetByID(ctx.Context(), id) @@ -193,7 +193,7 @@ func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) { } // SendTo -// @Router /v1/admin/posts/:id/send-to/:userId [post] +// @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 { diff --git a/backend/app/http/admin/routes.gen.go b/backend/app/http/admin/routes.gen.go index 560c066..7d3fa05 100644 --- a/backend/app/http/admin/routes.gen.go +++ b/backend/app/http/admin/routes.gen.go @@ -33,95 +33,95 @@ func (r *Routes) Name() string { func (r *Routes) Register(router fiber.Router) { // 注册路由组: auth - router.Post("/v1/admin/auth", DataFunc1( + router.Post("/admin/auth", DataFunc1( r.auth.Login, Body[AuthBody]("body"), )) // 注册路由组: medias - router.Get("/v1/admin/medias", DataFunc2( + router.Get("/admin/medias", DataFunc2( r.medias.List, Query[requests.Pagination]("pagination"), Query[ListQuery]("query"), )) - router.Get("/v1/admin/medias/:id", Func1( + router.Get("/admin/medias/:id", Func1( r.medias.Show, PathParam[int64]("id"), )) - router.Delete("/v1/admin/medias/:id", Func1( + router.Delete("/admin/medias/:id", Func1( r.medias.Delete, PathParam[int64]("id"), )) // 注册路由组: orders - router.Get("/v1/admin/orders", DataFunc2( + router.Get("/admin/orders", DataFunc2( r.orders.List, Query[requests.Pagination]("pagination"), Query[OrderListQuery]("query"), )) // 注册路由组: posts - router.Get("/v1/admin/posts", DataFunc2( + router.Get("/admin/posts", DataFunc2( r.posts.List, Query[requests.Pagination]("pagination"), Query[ListQuery]("query"), )) - router.Post("/v1/admin/posts", Func1( + router.Post("/admin/posts", Func1( r.posts.Create, Body[PostForm]("form"), )) - router.Put("/v1/admin/posts/:id", Func2( + router.Put("/admin/posts/:id", Func2( r.posts.Update, PathParam[int64]("id"), Body[PostForm]("form"), )) - router.Delete("/v1/admin/posts/:id", Func1( + router.Delete("/admin/posts/:id", Func1( r.posts.Delete, PathParam[int64]("id"), )) - router.Get("/v1/admin/posts/:id", DataFunc1( + router.Get("/admin/posts/:id", DataFunc1( r.posts.Show, PathParam[int64]("id"), )) - router.Post("/v1/admin/posts/:id/send-to/:userId", Func2( + router.Post("/admin/posts/:id/send-to/:userId", Func2( r.posts.SendTo, PathParam[int64]("id"), PathParam[int64]("userId"), )) // 注册路由组: uploads - router.Get("/v1/admin/uploads/pre-uploaded-check/:md5.:ext", DataFunc3( + router.Get("/admin/uploads/pre-uploaded-check/:md5.:ext", DataFunc3( r.uploads.PreUploadCheck, PathParam[string]("md5"), PathParam[string]("ext"), QueryParam[string]("mime"), )) - router.Post("/v1/admin/uploads/post-uploaded-action", Func1( + router.Post("/admin/uploads/post-uploaded-action", Func1( r.uploads.PostUploadedAction, Body[PostUploadedForm]("body"), )) // 注册路由组: users - router.Get("/v1/admin/users", DataFunc2( + router.Get("/admin/users", DataFunc2( r.users.List, Query[requests.Pagination]("pagination"), Query[UserListQuery]("query"), )) - router.Get("/v1/admin/users/:id", DataFunc1( + router.Get("/admin/users/:id", DataFunc1( r.users.Show, PathParam[int64]("id"), )) - router.Get("/v1/admin/users/:id/articles", DataFunc2( + router.Get("/admin/users/:id/articles", DataFunc2( r.users.Articles, PathParam[int64]("id"), Query[requests.Pagination]("pagination"), diff --git a/backend/app/http/admin/uploads.go b/backend/app/http/admin/uploads.go index 3dae274..ab9aa39 100644 --- a/backend/app/http/admin/uploads.go +++ b/backend/app/http/admin/uploads.go @@ -33,7 +33,7 @@ type PreCheckResp struct { } // PreUploadCheck -// @Router /v1/admin/uploads/pre-uploaded-check/:md5.:ext [get] +// @Router /admin/uploads/pre-uploaded-check/:md5.:ext [get] // @Bind md5 path // @Bind ext path // @Bind mime query @@ -59,7 +59,7 @@ type PostUploadedForm struct { } // PostUploadedAction -// @Router /v1/admin/uploads/post-uploaded-action [post] +// @Router /admin/uploads/post-uploaded-action [post] // @Bind body body func (up *uploads) PostUploadedAction(ctx fiber.Ctx, body *PostUploadedForm) error { m, err := models.Medias.GetByHash(ctx.Context(), body.Md5) diff --git a/backend/app/http/admin/users.go b/backend/app/http/admin/users.go index 2d0c845..f4c7e93 100644 --- a/backend/app/http/admin/users.go +++ b/backend/app/http/admin/users.go @@ -16,7 +16,7 @@ type UserListQuery struct { type users struct{} // List users -// @Router /v1/admin/users [get] +// @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) { @@ -25,14 +25,14 @@ func (ctl *users) List(ctx fiber.Ctx, pagination *requests.Pagination, query *Us } // Show user -// @Router /v1/admin/users/:id [get] +// @Router /admin/users/:id [get] // @Bind id path func (ctl *users) Show(ctx fiber.Ctx, id int64) (*model.Users, error) { return models.Users.GetByID(ctx.Context(), id) } // Articles show user bought articles -// @Router /v1/admin/users/:id/articles [get] +// @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) { diff --git a/backend/app/http/posts.go b/backend/app/http/posts.go index 7e49553..51f352b 100644 --- a/backend/app/http/posts.go +++ b/backend/app/http/posts.go @@ -8,6 +8,7 @@ import ( "quyun/app/requests" "quyun/database/fields" "quyun/database/schemas/public/model" + "quyun/providers/ali" "quyun/providers/wepay" "github.com/go-pay/gopay/wechat/v3" @@ -24,11 +25,7 @@ type ListQuery struct { // @provider type posts struct { wepay *wepay.Client -} - -type PostItem struct { - model.Posts - BoughtCount int64 `json:"bought_count"` + oss *ali.OSSClient } // List posts @@ -43,36 +40,90 @@ func (ctl *posts) List(ctx fiber.Ctx, pagination *requests.Pagination, query *Li item.Content = "" return item }) + if err != nil { + return nil, err + } - postIds := lo.Map(pager.Items.([]model.Posts), func(item model.Posts, _ int) int64 { - return item.ID - }) + postIds := lo.Map(pager.Items.([]model.Posts), func(item model.Posts, _ int) int64 { return item.ID }) if len(postIds) > 0 { - postCntMap, err := models.Posts.BoughtStatistics(ctx.Context(), postIds) - if err != nil { - return pager, err - } - - items := lo.Map(pager.Items.([]model.Posts), func(item model.Posts, _ int) PostItem { - cnt := int64(0) - if v, ok := postCntMap[item.ID]; ok { - cnt = v + items := lo.FilterMap(pager.Items.([]model.Posts), func(item model.Posts, _ int) (PostItem, bool) { + medias, err := models.Posts.GetMediaByIds(ctx.Context(), item.HeadImages.Data) + if err != nil { + log.Errorf("GetMediaByIds err: %v", err) + return PostItem{}, false } + mediaUrls := lo.FilterMap(medias, func(item model.Medias, _ int) (string, bool) { + url, err := ctl.oss.GetSignedUrl(ctx.Context(), item.Path) + if err != nil { + return "", false + } - return PostItem{Posts: item, BoughtCount: cnt} + return url, true + }) + + return PostItem{ + Title: item.Title, + Description: item.Description, + Price: item.Price, + Discount: item.Discount, + Views: item.Views, + Likes: item.Likes, + Tags: item.Tags.Data, + HeadImages: mediaUrls, + }, true }) pager.Items = items } - return pager, err + return pager, nil +} + +type PostItem struct { + Title string `json:"title"` + Description string `json:"description"` + Content string `json:"content"` + Price int64 `json:"price"` + Discount int16 `json:"discount"` + Views int64 `json:"views"` + Likes int64 `json:"likes"` + Tags []string `json:"tags"` + HeadImages []string `json:"head_images"` } // Show -// @Router /api/posts/show/:id [get] +// @Router /api/posts/:id [get] // @Bind id path -func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*model.Posts, error) { - return models.Posts.GetByID(ctx.Context(), id) +func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) { + post, err := models.Posts.GetByID(ctx.Context(), id) + if err != nil { + return nil, err + } + + medias, err := models.Posts.GetMediaByIds(ctx.Context(), post.HeadImages.Data) + if err != nil { + return nil, err + } + mediaUrls := lo.FilterMap(medias, func(item model.Medias, _ int) (string, bool) { + url, err := ctl.oss.GetSignedUrl(ctx.Context(), item.Path) + if err != nil { + return "", false + } + + return url, true + }) + + return &PostItem{ + Title: post.Title, + Description: post.Description, + Content: post.Content, + Price: post.Price, + Discount: post.Discount, + Views: post.Views, + Likes: post.Likes, + Tags: post.Tags.Data, + HeadImages: mediaUrls, + }, nil } // Mine posts @@ -89,7 +140,7 @@ func (ctl *posts) Mine(ctx fiber.Ctx, pagination *requests.Pagination, query *Li } // Buy -// @Router /api/posts/buy/:id [get] +// @Router /api/posts/:id/buy [get] // @Bind id path // @Bind user local func (ctl *posts) Buy(ctx fiber.Ctx, id int64, user *model.Users) (*wechat.JSAPIPayParams, error) { diff --git a/backend/app/http/provider.gen.go b/backend/app/http/provider.gen.go index 07c8fc8..8c00082 100755 --- a/backend/app/http/provider.gen.go +++ b/backend/app/http/provider.gen.go @@ -1,6 +1,7 @@ package http import ( + "quyun/providers/ali" "quyun/providers/job" "quyun/providers/jwt" "quyun/providers/wechat" @@ -40,9 +41,11 @@ func Provide(opts ...opt.Option) error { return err } if err := container.Container.Provide(func( + oss *ali.OSSClient, wepay *wepay.Client, ) (*posts, error) { obj := &posts{ + oss: oss, wepay: wepay, } diff --git a/backend/app/http/routes.gen.go b/backend/app/http/routes.gen.go index f30cbc7..3801da8 100644 --- a/backend/app/http/routes.gen.go +++ b/backend/app/http/routes.gen.go @@ -58,7 +58,7 @@ func (r *Routes) Register(router fiber.Router) { Local[*model.Users]("user"), )) - router.Get("/api/posts/show/:id", DataFunc1( + router.Get("/api/posts/:id", DataFunc1( r.posts.Show, PathParam[int64]("id"), )) @@ -69,7 +69,7 @@ func (r *Routes) Register(router fiber.Router) { Query[ListQuery]("query"), )) - router.Get("/api/posts/buy/:id", DataFunc2( + router.Get("/api/posts/:id/buy", DataFunc2( r.posts.Buy, PathParam[int64]("id"), Local[*model.Users]("user"), diff --git a/backend/app/middlewares/mid_auth.go b/backend/app/middlewares/mid_auth.go index de93ae4..a7c72c9 100644 --- a/backend/app/middlewares/mid_auth.go +++ b/backend/app/middlewares/mid_auth.go @@ -19,6 +19,13 @@ func (f *Middlewares) Auth(ctx fiber.Ctx) error { return ctx.Next() } + uu, err := models.Users.GetByID(ctx.Context(), 1) + if err != nil { + return ctx.SendString("NOT OK") + } + ctx.Locals("user", uu) + return ctx.Next() + fullUrl := string(ctx.Request().URI().FullURI()) u, err := url.Parse(fullUrl) if err != nil { diff --git a/backend/app/models/posts.go b/backend/app/models/posts.go index a28ca8f..bcfbb32 100644 --- a/backend/app/models/posts.go +++ b/backend/app/models/posts.go @@ -370,3 +370,28 @@ func (m *postsModel) GetPostsMapByIDs(ctx context.Context, ids []int64) (map[int return item.ID, item }), nil } + +// GetMediaByIds +func (m *postsModel) GetMediaByIds(ctx context.Context, ids []int64) ([]model.Medias, error) { + if len(ids) == 0 { + return nil, nil + } + + tbl := table.Medias + stmt := tbl. + SELECT(tbl.AllColumns). + WHERE( + tbl.ID.IN(lo.Map(ids, func(id int64, _ int) Expression { return Int64(id) })...), + ) + + m.log.Infof("sql: %s", stmt.DebugSql()) + + var medias []model.Medias + + if err := stmt.QueryContext(ctx, db, &medias); err != nil { + m.log.Errorf("error querying media: %v", err) + return nil, err + } + + return medias, nil +} diff --git a/frontend/wechat/src/api/client.js b/frontend/wechat/src/api/client.js index 8231b3b..39a2484 100644 --- a/frontend/wechat/src/api/client.js +++ b/frontend/wechat/src/api/client.js @@ -2,7 +2,7 @@ import axios from 'axios'; // Create axios instance with default config const client = axios.create({ - baseURL: '/v1', + baseURL: '/', timeout: 10000, headers: { 'Content-Type': 'application/json', diff --git a/frontend/wechat/src/api/post_detail.json b/frontend/wechat/src/api/post_detail.json new file mode 100644 index 0000000..2a40fb1 --- /dev/null +++ b/frontend/wechat/src/api/post_detail.json @@ -0,0 +1,17 @@ +{ + "title": "video1", + "description": "hello", + "content": "hello world", + "price": 100, + "discount": 100, + "views": 100, + "likes": 1000, + "tags": [ + "AAA", + "BBB", + "CCC" + ], + "head_images": [ + "https://assets.jdwan.com/quyun/b55adbd05ee96f5c53f9a19ad89a30cc.jpg?x-oss-credential=LTAI5t86SjiP9zRd3q2w7jQN%2F20250425%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-date=20250425T030400Z&x-oss-expires=300&x-oss-signature=fae33ecfc5cfe6d50de858d89798151b137336e3694bdafad78e1cbdb1ebf1c0&x-oss-signature-version=OSS4-HMAC-SHA256" + ] +} \ No newline at end of file diff --git a/frontend/wechat/src/views/ArticleDetail.vue b/frontend/wechat/src/views/ArticleDetail.vue index eaafdba..c0c3d46 100644 --- a/frontend/wechat/src/views/ArticleDetail.vue +++ b/frontend/wechat/src/views/ArticleDetail.vue @@ -47,13 +47,21 @@ const fetchArticle = async () => { } } +const formatDate = (dateString) => { + return new Date(dateString).toLocaleDateString('zh-CN', { + year: 'numeric', + month: 'long', + day: 'numeric' + }) +} + onMounted(async () => { await fetchArticle() }) \ No newline at end of file