From 013b939ac7fcd99007d79059c371bb8c374325db Mon Sep 17 00:00:00 2001 From: textworld Date: Wed, 22 Nov 2023 09:04:20 +0800 Subject: [PATCH] feat: add some api for wordpress --- consts/consts.go | 4 + handler/content/wp/category.go | 2 +- handler/content/wp/init.go | 1 + handler/content/wp/post.go | 132 +++++++++++++++++++++++++++------ handler/content/wp/tag.go | 53 +++++++++++++ handler/content/wp/user.go | 2 +- handler/router.go | 18 ++++- handler/server.go | 18 +++++ model/dto/base.go | 6 ++ model/dto/wp/tag.go | 12 +++ model/param/wp_post.go | 5 ++ model/param/wp_tag.go | 5 ++ service/impl/tag.go | 16 ++++ service/tag.go | 1 + 14 files changed, 249 insertions(+), 26 deletions(-) create mode 100644 handler/content/wp/tag.go create mode 100644 model/dto/wp/tag.go create mode 100644 model/param/wp_tag.go diff --git a/consts/consts.go b/consts/consts.go index 58fd491..9f503f0 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -46,3 +46,7 @@ var ( BuildTime string BuildCommit string ) + +const ( + LocalDateTimeFormat = "2006-01-02T15:04:05" +) diff --git a/handler/content/wp/category.go b/handler/content/wp/category.go index 5bb5b18..99561f5 100644 --- a/handler/content/wp/category.go +++ b/handler/content/wp/category.go @@ -27,7 +27,7 @@ func (c *CategoryHandler) List(ctx *gin.Context) (interface{}, error) { return nil, err } - categoryDTOList := make([]*wp.CategoryDTO, len(categoryEntities)) + categoryDTOList := make([]*wp.CategoryDTO, 0, len(categoryEntities)) for _, categoryEntity := range categoryEntities { categoryDTOList = append(categoryDTOList, convertToCategoryDTO(categoryEntity)) } diff --git a/handler/content/wp/init.go b/handler/content/wp/init.go index dabe91e..9309667 100644 --- a/handler/content/wp/init.go +++ b/handler/content/wp/init.go @@ -7,5 +7,6 @@ func init() { NewPostHandler, NewUserHandler, NewCategoryHandler, + NewTagHandler, ) } diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index 586d506..ac73983 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -3,13 +3,15 @@ package wp import ( "encoding/json" "github.com/gin-gonic/gin" - "github.com/go-sonic/sonic/consts" + sonicconst "github.com/go-sonic/sonic/consts" "github.com/go-sonic/sonic/log" "github.com/go-sonic/sonic/model/dto/wp" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" "github.com/go-sonic/sonic/service" "github.com/go-sonic/sonic/util" + "github.com/go-sonic/sonic/util/xerr" + "strconv" "strings" "time" ) @@ -24,50 +26,133 @@ func NewPostHandler(postService service.PostService) *PostHandler { } } +func (handler *PostHandler) List(ctx *gin.Context) (interface{}, error) { + var wpPostQuery param.WpPostQuery + if err := ctx.ShouldBind(&wpPostQuery); err != nil { + return nil, util.WrapJsonBindErr(err) + } + + var postQuery param.PostQuery + postQuery.PageSize = wpPostQuery.Page + postQuery.PageNum = wpPostQuery.PerPage + entities, _, err := handler.PostService.Page(ctx, postQuery) + if err != nil { + return nil, err + } + + wpPostList := make([]*wp.PostDTO, 0, len(entities)) + for _, postEntity := range entities { + wpPostList = append(wpPostList, convertToWpPost(postEntity)) + } + + return wpPostList, nil +} + func (handler *PostHandler) Create(ctx *gin.Context) (interface{}, error) { - var wpPost param.WpPost - err := ctx.ShouldBindJSON(&wpPost) + postParam, err := parsePostParam(ctx) if err != nil { return nil, util.WrapJsonBindErr(err) } - bytes, err := json.Marshal(wpPost) + create, err := handler.PostService.Create(ctx, postParam) if err != nil { return nil, err } - log.CtxInfo(ctx, "wpPost: "+string(bytes)) + return convertToWpPost(create), nil +} - postParam := convertToPostParam(&wpPost) +func (handler *PostHandler) Update(ctx *gin.Context) (interface{}, error) { + postParam, err := parsePostParam(ctx) + if err != nil { + return nil, util.WrapJsonBindErr(err) + } - create, err := handler.PostService.Create(ctx, postParam) + postIDStr := ctx.Param("postID") + postID, err := strconv.ParseInt(postIDStr, 10, 32) + if err != nil { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("Parameter error") + } + + postDetail, err := handler.PostService.Update(ctx, int32(postID), postParam) if err != nil { return nil, err } - return convertToWpPost(create), nil + return convertToWpPost(postDetail), nil +} + +func (handler *PostHandler) Delete(ctx *gin.Context) (interface{}, error) { + postIDStr := ctx.Param("postID") + postID, err := strconv.ParseInt(postIDStr, 10, 32) + if err != nil { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("Parameter error") + } + + postEntity, err := handler.PostService.GetByPostID(ctx, int32(postID)) + if err != nil { + return nil, err + } + + if err = handler.PostService.Delete(ctx, int32(postID)); err != nil { + return nil, err + } + + return convertToWpPost(postEntity), nil +} + +func parsePostParam(ctx *gin.Context) (*param.Post, error) { + var wpPost param.WpPost + err := ctx.ShouldBindJSON(&wpPost) + if err != nil { + return nil, util.WrapJsonBindErr(err) + } + + bytes, err := json.Marshal(wpPost) + if err != nil { + return nil, err + } + log.CtxInfo(ctx, "wpPost: "+string(bytes)) + + return convertToPostParam(&wpPost) + } -func convertToPostParam(wpPost *param.WpPost) *param.Post { - var paramPostStatus = consts.PostStatusPublished - if strings.ToLower(wpPost.Content) == "draft" { - paramPostStatus = consts.PostStatusDraft +func convertToPostParam(wpPost *param.WpPost) (*param.Post, error) { + var paramPostStatus = sonicconst.PostStatusPublished + if strings.ToLower(wpPost.Status) == "draft" { + paramPostStatus = sonicconst.PostStatusDraft } - createTime := time.Now().Unix() + editorType := sonicconst.EditorTypeRichText + disallowComment := false + if strings.ToLower(wpPost.CommentStatus) == "closed" { + disallowComment = true + } + + var createTime *int64 + + if strings.TrimSpace(wpPost.Date) != "" { + datetime, err := time.Parse(sonicconst.LocalDateTimeFormat, wpPost.Date) + if err != nil { + return nil, err + } + dateTimeMills := datetime.UnixMilli() + createTime = &dateTimeMills + } return ¶m.Post{ Title: wpPost.Title, Status: paramPostStatus, Slug: wpPost.Slug, - EditorType: nil, + EditorType: &editorType, OriginalContent: wpPost.Content, Summary: "", Thumbnail: "", - DisallowComment: false, + DisallowComment: disallowComment, Password: wpPost.Password, Template: "", TopPriority: 0, - CreateTime: &createTime, + CreateTime: createTime, MetaKeywords: "", MetaDescription: "", TagIDs: make([]int32, 0), @@ -76,7 +161,7 @@ func convertToPostParam(wpPost *param.WpPost) *param.Post { Content: wpPost.Content, EditTime: nil, UpdateTime: nil, - } + }, nil } func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { @@ -85,7 +170,7 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { var wpCommentStatus = "open" var wpContent = make(map[string]interface{}) - if postEntity.Status == consts.PostStatusDraft { + if postEntity.Status == sonicconst.PostStatusDraft { wpStatus = "draft" } @@ -102,15 +187,15 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { Guid: nil, Id: postEntity.ID, Link: "", - Modified: postEntity.UpdateTime.Format(timeFormat), - ModifiedGmt: postEntity.UpdateTime.UTC().Format(timeFormat), + Modified: "", + ModifiedGmt: "", Slug: "", Status: wpStatus, Type: "post", Password: "standard", PermalinkTemplate: "", GeneratedSlug: "", - Title: "", + Title: postEntity.Title, Content: wpContent, Author: 0, Excerpt: nil, @@ -124,5 +209,10 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { Categories: make([]int32, 0), Tags: make([]int32, 0), } + + if postEntity.UpdateTime != nil { + postDTO.Modified = postEntity.UpdateTime.Format(timeFormat) + postDTO.ModifiedGmt = postEntity.UpdateTime.UTC().Format(timeFormat) + } return postDTO } diff --git a/handler/content/wp/tag.go b/handler/content/wp/tag.go new file mode 100644 index 0000000..8bcd166 --- /dev/null +++ b/handler/content/wp/tag.go @@ -0,0 +1,53 @@ +package wp + +import ( + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto/wp" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" +) + +type TagHandler struct { + TagService service.TagService +} + +func NewTagHandler(tagService service.TagService) *TagHandler { + return &TagHandler{ + TagService: tagService, + } +} + +func (handler *TagHandler) List(ctx *gin.Context) (interface{}, error) { + var err error + var listParam param.TagListParam + if err = ctx.ShouldBindJSON(&listParam); err != nil { + return nil, err + } + + entities, err := handler.TagService.ListByOption(ctx, &listParam) + if err != nil { + return nil, err + } + + tagDTOList := make([]*wp.TagDTO, 0, len(entities)) + for _, tagEntity := range entities { + tagDTOList = append(tagDTOList, convertToWpTag(tagEntity)) + } + + return tagDTOList, nil +} + +func convertToWpTag(tagEntity *entity.Tag) *wp.TagDTO { + tagDTO := &wp.TagDTO{ + ID: tagEntity.ID, + Count: 0, + Description: "", + Link: "", + Name: tagEntity.Name, + Slug: tagEntity.Slug, + Taxonomy: "", + Meta: nil, + } + return tagDTO +} diff --git a/handler/content/wp/user.go b/handler/content/wp/user.go index 1320243..07cdb08 100644 --- a/handler/content/wp/user.go +++ b/handler/content/wp/user.go @@ -22,7 +22,7 @@ func (u *UserHandler) List(ctx *gin.Context) (interface{}, error) { return nil, err } - userDTOList := make([]*dto.User, len(allUser)) + userDTOList := make([]*dto.User, 0, len(allUser)) for _, user := range allUser { userDTO := u.UserService.ConvertToDTO(ctx, user) userDTOList = append(userDTOList, userDTO) diff --git a/handler/router.go b/handler/router.go index 6484ed9..ab36122 100644 --- a/handler/router.go +++ b/handler/router.go @@ -47,9 +47,21 @@ func (s *Server) RegisterRouters() { { wpCompatibleRouter := router.Group("/wp-json/wp/v2") wpCompatibleRouter.Use(s.ApplicationPasswordMiddleware.GetWrapHandler()) - wpCompatibleRouter.POST("/posts", s.wrapHandler(s.WpPostHandler.Create)) - wpCompatibleRouter.GET("/users", s.wrapHandler(s.WpUserHandler.List)) - wpCompatibleRouter.GET("/categories", s.wrapHandler(s.WpCategoryHandler.List)) + { + wpCompatibleRouter.GET("/posts", s.wrapWpHandler(s.WpPostHandler.List)) + wpCompatibleRouter.POST("/posts", s.wrapWpHandler(s.WpPostHandler.Create)) + wpCompatibleRouter.POST("/posts/:postID", s.wrapWpHandler(s.WpPostHandler.Update)) + wpCompatibleRouter.DELETE("/posts/:postID", s.wrapWpHandler(s.WpPostHandler.Delete)) + } + { + wpCompatibleRouter.GET("/tags", s.wrapWpHandler(s.WpTagHandler.List)) + } + { + wpCompatibleRouter.GET("/users", s.wrapWpHandler(s.WpUserHandler.List)) + } + { + wpCompatibleRouter.GET("/categories", s.wrapWpHandler(s.WpCategoryHandler.List)) + } } { adminAPIRouter := router.Group("/api/admin") diff --git a/handler/server.go b/handler/server.go index c25ac4e..908d4a3 100644 --- a/handler/server.go +++ b/handler/server.go @@ -83,6 +83,7 @@ type Server struct { WpPostHandler *wp.PostHandler WpUserHandler *wp.UserHandler WpCategoryHandler *wp.CategoryHandler + WpTagHandler *wp.TagHandler } type ServerParams struct { @@ -143,6 +144,7 @@ type ServerParams struct { WpPostHandler *wp.PostHandler WpUserHandler *wp.UserHandler WpCategoryHandler *wp.CategoryHandler + WpTagHandler *wp.TagHandler } func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { @@ -213,6 +215,7 @@ func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { WpPostHandler: param.WpPostHandler, WpUserHandler: param.WpUserHandler, WpCategoryHandler: param.WpCategoryHandler, + WpTagHandler: param.WpTagHandler, } lifecycle.Append(fx.Hook{ OnStop: httpServer.Shutdown, @@ -256,6 +259,21 @@ func (s *Server) wrapHandler(handler wrapperHandler) gin.HandlerFunc { } } +func (s *Server) wrapWpHandler(handler wrapperHandler) gin.HandlerFunc { + return func(ctx *gin.Context) { + data, err := handler(ctx) + if err != nil { + s.logger.Error("handler error", zap.Error(err)) + status := xerr.GetHTTPStatus(err) + ctx.JSON(status, &dto.BaseWpDTO{Code: status, Message: xerr.GetMessage(err), Data: map[string]interface{}{"status": status}}) + return + } + + ctx.JSON(http.StatusOK, data) + return + } +} + type wrapperHTMLHandler func(ctx *gin.Context, model template.Model) (templateName string, err error) var ( diff --git a/model/dto/base.go b/model/dto/base.go index 7b72b15..8b61c72 100644 --- a/model/dto/base.go +++ b/model/dto/base.go @@ -14,6 +14,12 @@ type BaseDTO struct { Data interface{} `json:"data"` } +type BaseWpDTO struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + type Page struct { Content interface{} `json:"content"` Pages int `json:"pages"` diff --git a/model/dto/wp/tag.go b/model/dto/wp/tag.go new file mode 100644 index 0000000..d11c7ca --- /dev/null +++ b/model/dto/wp/tag.go @@ -0,0 +1,12 @@ +package wp + +type TagDTO struct { + ID int32 `json:"id"` + Count int32 `json:"count"` + Description string `json:"description"` + Link string `json:"link"` + Name string `json:"name"` + Slug string `json:"slug"` + Taxonomy string `json:"taxonomy"` + Meta map[string]interface{} `json:"meta"` +} diff --git a/model/param/wp_post.go b/model/param/wp_post.go index 3eaaeb8..05f4aac 100644 --- a/model/param/wp_post.go +++ b/model/param/wp_post.go @@ -28,3 +28,8 @@ type WpPost struct { Categories []int32 `json:"categories"` Tags []int32 `json:"tags"` } + +type WpPostQuery struct { + Page int `form:"page" default:"1"` + PerPage int `form:"per_page" default:"10"` +} diff --git a/model/param/wp_tag.go b/model/param/wp_tag.go new file mode 100644 index 0000000..dacefe8 --- /dev/null +++ b/model/param/wp_tag.go @@ -0,0 +1,5 @@ +package param + +type TagListParam struct { + Search string `json:"search"` +} diff --git a/service/impl/tag.go b/service/impl/tag.go index 9b349d1..0bcf4ad 100644 --- a/service/impl/tag.go +++ b/service/impl/tag.go @@ -230,3 +230,19 @@ func (t tagServiceImpl) GetByName(ctx context.Context, name string) (*entity.Tag tag, err := tagDAL.WithContext(ctx).Where(tagDAL.Name.Eq(name)).First() return tag, WrapDBErr(err) } + +func (t tagServiceImpl) ListByOption(ctx context.Context, option *param.TagListParam) ([]*entity.Tag, error) { + tagDAL := dal.GetQueryByCtx(ctx).Tag + if option == nil { + return tagDAL.WithContext(ctx).Where().Find() + } + + query := tagDAL.WithContext(ctx) + + search := strings.TrimSpace(option.Search) + if search != "" { + query.Where(tagDAL.Name.Like("%" + search + "%")) + } + + return query.Find() +} diff --git a/service/tag.go b/service/tag.go index 0a2800b..299c6dd 100644 --- a/service/tag.go +++ b/service/tag.go @@ -20,4 +20,5 @@ type TagService interface { Update(ctx context.Context, id int32, tagParam *param.Tag) (*entity.Tag, error) Delete(ctx context.Context, id int32) error CountAllTag(ctx context.Context) (int64, error) + ListByOption(ctx context.Context, option *param.TagListParam) ([]*entity.Tag, error) }