From 922ba7cfec3a2d7fdffeb93cfe1002a2fff9ca63 Mon Sep 17 00:00:00 2001 From: nurulalyh Date: Fri, 24 Nov 2023 22:46:20 +0800 Subject: [PATCH 1/2] feature(article): get article by id, manage article views, filter article by date range --- module/entities/category_models.go | 6 +- module/entities/product_models.go | 12 +- module/entities/reviews_models.go | 4 +- module/entities/users_models.go | 6 +- module/feature/article/dto/response.go | 2 + module/feature/article/handler/handler.go | 187 ++++++++++-------- module/feature/article/interface.go | 23 ++- .../feature/article/repository/repository.go | 86 +++++--- module/feature/article/service/service.go | 158 +++++++++------ routes/routes.go | 1 + 10 files changed, 301 insertions(+), 184 deletions(-) diff --git a/module/entities/category_models.go b/module/entities/category_models.go index d3ce0bc..71ebb08 100644 --- a/module/entities/category_models.go +++ b/module/entities/category_models.go @@ -7,9 +7,9 @@ type CategoryModels struct { Name string `gorm:"column:name;type:varchar(255)" json:"name"` Photo string `gorm:"column:photo;type:varchar(255)" json:"photo"` TotalProduct uint64 `gorm:"column:total_product;type:BIGINT UNSIGNED" json:"total_product"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` Products []ProductModels `gorm:"many2many:product_categories;" json:"products"` } diff --git a/module/entities/product_models.go b/module/entities/product_models.go index a0da814..9ffd6fa 100644 --- a/module/entities/product_models.go +++ b/module/entities/product_models.go @@ -15,9 +15,9 @@ type ProductModels struct { Exp uint64 `gorm:"column:exp;type:BIGINT UNSIGNED" json:"product_exp"` Rating float64 `gorm:"column:rating;type:DECIMAL(3, 1)" json:"rating"` TotalReview uint64 `gorm:"column:total_review;type:BIGINT UNSIGNED" json:"total_review"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` ProductPhotos []ProductPhotosModels `gorm:"foreignKey:ProductID" json:"product_photos"` ProductReview []ReviewModels `gorm:"foreignKey:ProductID;references:ID" json:"review"` Categories []CategoryModels `gorm:"many2many:product_categories;" json:"categories"` @@ -27,9 +27,9 @@ type ProductPhotosModels struct { ID uint64 `gorm:"column:id;type:bigint;primaryKey" json:"id"` ProductID uint64 `gorm:"column:product_id;type:BIGINT UNSIGNED" json:"product_id"` ImageURL string `gorm:"column:url;type:varchar(255)" json:"url"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` } func (ProductModels) TableName() string { diff --git a/module/entities/reviews_models.go b/module/entities/reviews_models.go index f62f1d6..60410b3 100644 --- a/module/entities/reviews_models.go +++ b/module/entities/reviews_models.go @@ -11,7 +11,7 @@ type ReviewModels struct { Date time.Time `gorm:"column:date;type:DATETIME" json:"date"` CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` Photos []ReviewPhotoModels `gorm:"foreignKey:ReviewID" json:"photos"` } @@ -21,7 +21,7 @@ type ReviewPhotoModels struct { ImageURL string `gorm:"column:url;type:varchar(255)" json:"url"` CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` } type ReviewDetail struct { diff --git a/module/entities/users_models.go b/module/entities/users_models.go index ca17638..f273a16 100644 --- a/module/entities/users_models.go +++ b/module/entities/users_models.go @@ -14,9 +14,9 @@ type UserModels struct { Level string `gorm:"column:level;type:VARCHAR(255)" json:"level"` Exp uint64 `gorm:"column:exp;type:BIGINT UNSIGNED" json:"exp"` IsVerified bool `gorm:"column:is_verified;default:false" json:"is_verified"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at;index" json:"deleted_at"` + CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP;index" json:"deleted_at"` Address []AddressModels `gorm:"foreignKey:UserID" json:"addresses"` Articles []ArticleModels `gorm:"many2many:user_bookmarks;" json:"users"` Reviews []ReviewModels `gorm:"foreignKey:UserID" json:"reviews"` diff --git a/module/feature/article/dto/response.go b/module/feature/article/dto/response.go index 6ed2662..c7263c0 100644 --- a/module/feature/article/dto/response.go +++ b/module/feature/article/dto/response.go @@ -13,6 +13,7 @@ type ArticleFormatter struct { Content string `json:"content"` Author string `json:"author"` Date time.Time `json:"date"` + Views uint64 `json:"views"` } func FormatArticle(article entities.ArticleModels) ArticleFormatter { @@ -23,6 +24,7 @@ func FormatArticle(article entities.ArticleModels) ArticleFormatter { articleFormatter.Content = article.Content articleFormatter.Author = article.Author articleFormatter.Date = article.UpdatedAt + articleFormatter.Views = article.Views return articleFormatter } diff --git a/module/feature/article/handler/handler.go b/module/feature/article/handler/handler.go index f47a989..2c40a14 100644 --- a/module/feature/article/handler/handler.go +++ b/module/feature/article/handler/handler.go @@ -27,8 +27,9 @@ func (h *ArticleHandler) CreateArticle() echo.HandlerFunc { return func(c echo.Context) error { currentUser := c.Get("CurrentUser").(*entities.UserModels) if currentUser.Role != "admin" { - return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan:: Anda tidak memiliki izin") + return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan: Anda tidak memiliki izin") } + articleRequest := new(dto.CreateArticleRequest) file, err := c.FormFile("photo") var uploadedURL string @@ -37,6 +38,7 @@ func (h *ArticleHandler) CreateArticle() echo.HandlerFunc { if err != nil { return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal membuka file: "+err.Error()) } + defer func(fileToUpload multipart.File) { _ = fileToUpload.Close() }(fileToUpload) @@ -48,7 +50,7 @@ func (h *ArticleHandler) CreateArticle() echo.HandlerFunc { } if err := c.Bind(articleRequest); err != nil { - return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai.") + return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai: "+err.Error()) } if err := utils.ValidateStruct(articleRequest); err != nil { @@ -62,64 +64,27 @@ func (h *ArticleHandler) CreateArticle() echo.HandlerFunc { } createdArticle, err := h.service.CreateArticle(newArticle) - if err!= nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Kesalahan Server Internal: "+err.Error()) - } - return response.SendSuccessResponse(c, "Berhasil menambahkan artikel", dto.FormatArticle(*createdArticle)) - } -} - -func (h *ArticleHandler) GetAllArticles() echo.HandlerFunc { - return func(c echo.Context) error { - page, _ := strconv.Atoi(c.QueryParam("page")) - pageConv, _ := strconv.Atoi(strconv.Itoa(page)) - perPage := 10 - - var articles []entities.ArticleModels - var totalItems int64 - var err error - search := c.QueryParam("search") - if search != "" { - articles, totalItems, err = h.service.GetArticlesByTitle(page, perPage, search) - if err != nil { - c.Logger().Error("handler: failed to fetch articles by title:", err.Error()) - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error") - } - } else { - articles, totalItems, err = h.service.GetAll(pageConv, perPage) - } if err != nil { - c.Logger().Error("handler: failed to fetch all articles:", err.Error()) - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error") + return response.SendErrorResponse(c, http.StatusInternalServerError, "Kesalahan Server Internal: "+err.Error()) } - current_page, total_pages := h.service.CalculatePaginationValues(pageConv, int(totalItems), perPage) - nextPage := h.service.GetNextPage(current_page, total_pages) - prevPage := h.service.GetPrevPage(current_page) - - return response.Pagination(c, dto.FormatterArticle(articles), current_page, total_pages, int(totalItems), nextPage, prevPage, "Daftar artikel") + return response.SendSuccessResponse(c, "Berhasil menambahkan artikel", dto.FormatArticle(*createdArticle)) } } func (h *ArticleHandler) UpdateArticleById() echo.HandlerFunc { return func(c echo.Context) error { currentUser := c.Get("CurrentUser").(*entities.UserModels) - if currentUser.Role!= "admin" { - return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan:: Anda tidak memiliki izin") - } + if currentUser.Role != "admin" { + return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan: Anda tidak memiliki izin") + } + updateRequest := new(dto.UpdateArticleRequest) articleID, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err!= nil { - return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai.") - } - - exitingArticle, err := h.service.GetArticleById(articleID) - if err!= nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mendapatkan artikel: "+err.Error()) - } - if exitingArticle == nil { - return response.SendErrorResponse(c, http.StatusNotFound, "Artikel tidak ditemukan"+err.Error()) - } + if err != nil { + return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai: "+err.Error()) + } + file, err := c.FormFile("photo") var uploadedURL string if err == nil { @@ -127,6 +92,7 @@ func (h *ArticleHandler) UpdateArticleById() echo.HandlerFunc { if err != nil { return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal membuka file: "+err.Error()) } + defer func(fileToUpload multipart.File) { _ = fileToUpload.Close() }(fileToUpload) @@ -136,22 +102,26 @@ func (h *ArticleHandler) UpdateArticleById() echo.HandlerFunc { return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mengunggah foto: "+err.Error()) } } - if err := c.Bind(updateRequest); err!= nil { - return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai.") - } - if err := utils.ValidateStruct(updateRequest); err!= nil { - return response.SendErrorResponse(c, http.StatusBadRequest, "Validasi gagal: "+err.Error()) - } + if err := c.Bind(updateRequest); err != nil { + return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai: "+err.Error()) + } + + if err := utils.ValidateStruct(updateRequest); err != nil { + return response.SendErrorResponse(c, http.StatusBadRequest, "Validasi gagal: "+err.Error()) + } + newData := &entities.ArticleModels{ Title: updateRequest.Title, - Photo: uploadedURL, - Content: updateRequest.Content, - } + Photo: uploadedURL, + Content: updateRequest.Content, + } + updatedArticle, err := h.service.UpdateArticleById(articleID, newData) - if err!= nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mengubah article: "+err.Error()) - } + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mengubah artikel: "+err.Error()) + } + return response.SendSuccessResponse(c, "Berhasil mengubah artikel", dto.FormatArticle(*updatedArticle)) } } @@ -159,27 +129,80 @@ func (h *ArticleHandler) UpdateArticleById() echo.HandlerFunc { func (h *ArticleHandler) DeleteArticleById() echo.HandlerFunc { return func(c echo.Context) error { currentUser := c.Get("CurrentUser").(*entities.UserModels) - if currentUser.Role!= "admin" { - return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan:: Anda tidak memiliki izin") - } + if currentUser.Role != "admin" { + return response.SendErrorResponse(c, http.StatusUnauthorized, "Tidak diizinkan: Anda tidak memiliki izin") + } articleID, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err!= nil { - return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai.") - } - - exitingArticle, err := h.service.GetArticleById(articleID) - if err!= nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mendapatkan artikel: "+err.Error()) - } - if exitingArticle == nil { - return response.SendErrorResponse(c, http.StatusNotFound, "Artikel tidak ditemukan"+err.Error()) - } - - err = h.service.DeleteArticleById(articleID) - if err!= nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal menghapus artikel: "+err.Error()) - } - return response.SendStatusOkResponse(c, "Berhasil menghapus artikel") - } + if err != nil { + return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai: "+err.Error()) + } + + err = h.service.DeleteArticleById(articleID) + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal menghapus artikel: "+err.Error()) + } + + return response.SendStatusOkResponse(c, "Berhasil menghapus artikel") + } +} + +func (h *ArticleHandler) GetAllArticles() echo.HandlerFunc { + return func(c echo.Context) error { + page, _ := strconv.Atoi(c.QueryParam("page")) + pageConv, _ := strconv.Atoi(strconv.Itoa(page)) + perPage := 10 + + var articles []entities.ArticleModels + var totalItems int64 + var err error + search := c.QueryParam("search") + filterType := c.QueryParam("filter_type") + if search != "" { + articles, totalItems, err = h.service.GetArticlesByTitle(page, perPage, search) + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + } + } else if filterType != "" { + articles, totalItems, err = h.service.GetArticlesByDateRange(page, perPage, filterType) + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + } + } else { + articles, totalItems, err = h.service.GetAll(pageConv, perPage) + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + } + } + + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + } + + current_page, total_pages := h.service.CalculatePaginationValues(pageConv, int(totalItems), perPage) + nextPage := h.service.GetNextPage(current_page, total_pages) + prevPage := h.service.GetPrevPage(current_page) + + return response.Pagination(c, dto.FormatterArticle(articles), current_page, total_pages, int(totalItems), nextPage, prevPage, "Daftar artikel") + } +} + +func (h *ArticleHandler) GetArticleById() echo.HandlerFunc { + return func(c echo.Context) error { + id := c.Param("id") + articleID, err := strconv.ParseUint(id, 10, 64) + if err != nil { + return response.SendErrorResponse(c, http.StatusBadRequest, "Format input yang Anda masukkan tidak sesuai: " + err.Error()) + } + + currentUser := c.Get("CurrentUser").(*entities.UserModels) + incrementViews := currentUser.Role != "admin" + + getArticleID, err := h.service.GetArticleById(articleID, incrementViews) + if err != nil { + return response.SendErrorResponse(c, http.StatusInternalServerError, "Gagal mengambil artikel: " + err.Error()) + } + + return response.SendSuccessResponse(c, "Detail artikel", dto.FormatArticle(*getArticleID)) + } } \ No newline at end of file diff --git a/module/feature/article/interface.go b/module/feature/article/interface.go index 4ced68c..2370c3f 100644 --- a/module/feature/article/interface.go +++ b/module/feature/article/interface.go @@ -1,36 +1,43 @@ package article import ( + "time" + "github.com/capstone-kelompok-7/backend-disappear/module/entities" "github.com/labstack/echo/v4" ) type RepositoryArticleInterface interface { CreateArticle(article *entities.ArticleModels) (*entities.ArticleModels, error) - GetArticleById(id uint64) (*entities.ArticleModels, error) + UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) + UpdateArticleViews(article *entities.ArticleModels) error + DeleteArticleById(id uint64) error FindAll(page, perpage int) ([]entities.ArticleModels, error) GetTotalArticleCount() (int64, error) FindByTitle(page, perpage int, title string) ([]entities.ArticleModels, error) GetTotalArticleCountByTitle(title string) (int64, error) - UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) - DeleteArticleById(id uint64) error + GetArticleById(id uint64) (*entities.ArticleModels, error) + GetArticlesByDateRange(page, perpage int, startDate, endDate time.Time) ([]entities.ArticleModels, error) + GetTotalArticleCountByDateRange(startDate, endDate time.Time) (int64, error) } type ServiceArticleInterface interface { CreateArticle(articleData *entities.ArticleModels) (*entities.ArticleModels, error) + UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) + DeleteArticleById(id uint64) error GetAll(page, perPage int) ([]entities.ArticleModels, int64, error) + GetArticlesByTitle(page, perPage int, title string) ([]entities.ArticleModels, int64, error) + GetArticleById(id uint64, incrementVIews bool) (*entities.ArticleModels, error) + GetArticlesByDateRange(page, perPage int, filterType string) ([]entities.ArticleModels, int64, error) CalculatePaginationValues(page int, totalItems int, perPage int) (int, int) GetNextPage(currentPage, totalPages int) int GetPrevPage(currentPage int) int - GetArticlesByTitle(page, perPage int, title string) ([]entities.ArticleModels, int64, error) - GetArticleById(id uint64) (*entities.ArticleModels, error) - UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) - DeleteArticleById(id uint64) error } type HandlerArticleInterface interface { CreateArticle() echo.HandlerFunc - GetAllArticles() echo.HandlerFunc UpdateArticleById() echo.HandlerFunc DeleteArticleById() echo.HandlerFunc + GetAllArticles() echo.HandlerFunc + GetArticleById() echo.HandlerFunc } diff --git a/module/feature/article/repository/repository.go b/module/feature/article/repository/repository.go index 84a1676..05f4b12 100644 --- a/module/feature/article/repository/repository.go +++ b/module/feature/article/repository/repository.go @@ -1,6 +1,8 @@ package repository import ( + "time" + "github.com/capstone-kelompok-7/backend-disappear/module/entities" "github.com/capstone-kelompok-7/backend-disappear/module/feature/article" "gorm.io/gorm" @@ -24,12 +26,40 @@ func (r *ArticleRepository) CreateArticle(article *entities.ArticleModels) (*ent return article, nil } -func (r *ArticleRepository) GetArticleById(id uint64) (*entities.ArticleModels, error) { +func (r *ArticleRepository) UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) { var article entities.ArticleModels - if err := r.db.Where("id =? AND deleted_at IS NULL", id).First(&article).Error; err != nil { + if err := r.db.First(&article, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } return nil, err } - return &article, nil + + if err := r.db.Model(&article).Updates(updatedArticle).Error; err != nil { + return nil, err + } + + return updatedArticle, nil +} + +func (r *ArticleRepository) UpdateArticleViews(article *entities.ArticleModels) error { + return r.db.Save(article).Error +} + +func (r *ArticleRepository) DeleteArticleById(id uint64) error { + var article entities.ArticleModels + if err := r.db.First(&article, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil + } + return err + } + + if err := r.db.Delete(&article).Error; err != nil { + return err + } + + return nil } func (r *ArticleRepository) FindAll(page, perpage int) ([]entities.ArticleModels, error) { @@ -37,15 +67,20 @@ func (r *ArticleRepository) FindAll(page, perpage int) ([]entities.ArticleModels offset := (page - 1) * perpage err := r.db.Offset(offset).Limit(perpage).Find(&articles).Error if err != nil { - return articles, err + return nil, err } + return articles, nil } func (r *ArticleRepository) GetTotalArticleCount() (int64, error) { var count int64 err := r.db.Model(&entities.ArticleModels{}).Count(&count).Error - return count, err + if err != nil { + return 0, err + } + + return count, nil } func (r *ArticleRepository) FindByTitle(page, perpage int, title string) ([]entities.ArticleModels, error) { @@ -53,44 +88,45 @@ func (r *ArticleRepository) FindByTitle(page, perpage int, title string) ([]enti offset := (page - 1) * perpage err := r.db.Offset(offset).Limit(perpage).Where("title LIKE?", "%"+title+"%").Find(&articles).Error if err != nil { - return articles, err + return nil, err } + return articles, nil } func (r *ArticleRepository) GetTotalArticleCountByTitle(title string) (int64, error) { var count int64 err := r.db.Model(&entities.ArticleModels{}).Where("title LIKE?", "%"+title+"%").Count(&count).Error + if err != nil { + return 0, err + } + return count, err } -func (r *ArticleRepository) UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) { +func (r *ArticleRepository) GetArticleById(id uint64) (*entities.ArticleModels, error) { var article entities.ArticleModels - if err := r.db.First(&article, id).Error; err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } + if err := r.db.Where("id =? AND deleted_at IS NULL", id).First(&article).Error; err != nil { return nil, err } + return &article, nil +} - if err := r.db.Model(&article).Updates(updatedArticle).Error; err != nil { +func (r *ArticleRepository) GetArticlesByDateRange(page, perpage int, startDate, endDate time.Time) ([]entities.ArticleModels, error) { + var articles []entities.ArticleModels + offset := (page - 1) * perpage + if err := r.db.Offset(offset).Limit(perpage).Where("updated_at BETWEEN ? AND ?", startDate, endDate).Find(&articles).Error; err != nil { return nil, err } - - return updatedArticle, nil + return articles, nil } -func (r *ArticleRepository) DeleteArticleById(id uint64) error { - var article entities.ArticleModels - if err := r.db.First(&article, id).Error; err != nil { - if err == gorm.ErrRecordNotFound { - return nil - } - return err - } - if err := r.db.Delete(&article).Error; err != nil { - return err +func (r *ArticleRepository) GetTotalArticleCountByDateRange(startDate, endDate time.Time) (int64, error) { + var count int64 + err := r.db.Model(&entities.ArticleModels{}).Where("updated_at BETWEEN ? AND ?", startDate, endDate).Count(&count).Error + if err != nil { + return 0, err } - return nil + return count, err } diff --git a/module/feature/article/service/service.go b/module/feature/article/service/service.go index 112731d..9e936f7 100644 --- a/module/feature/article/service/service.go +++ b/module/feature/article/service/service.go @@ -3,6 +3,7 @@ package service import ( "errors" "math" + "time" "github.com/capstone-kelompok-7/backend-disappear/module/entities" "github.com/capstone-kelompok-7/backend-disappear/module/feature/article" @@ -23,113 +24,160 @@ func (s *ArticleService) CreateArticle(articleData *entities.ArticleModels) (*en Title: articleData.Title, Photo: articleData.Photo, Content: articleData.Content, - Author: "DISAPPEAR", + Author: "DISAPPEAR", } createdArticle, err := s.repo.CreateArticle(value) if err != nil { - return nil, err + return nil, errors.New("Gagal Menambahkan Artikel: " + err.Error()) } return createdArticle, nil } -func (s *ArticleService) GetAll(page, perPage int) ([]entities.ArticleModels, int64, error) { - articles, err := s.repo.FindAll(page, perPage) +func (s *ArticleService) UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) { + existingArticle, err := s.repo.GetArticleById(id) if err != nil { - return articles, 0, err + return nil, errors.New("artikel tidak ditemukan: " + err.Error()) } - totalItems, err := s.repo.GetTotalArticleCount() + if existingArticle == nil { + return nil, errors.New("artikel tidak ditemukan:" + err.Error()) + } + + updatedArticle, err = s.repo.UpdateArticleById(id, updatedArticle) if err != nil { - return articles, 0, err + return nil, errors.New("Gagal Mengubah Artikel: " + err.Error()) } - return articles, totalItems, nil + getUpdatedArticle, err := s.repo.GetArticleById(id) + + return getUpdatedArticle, nil } -func (s *ArticleService) CalculatePaginationValues(page int, totalItems int, perPage int) (int, int) { - pageInt := page - if pageInt <= 0 { - pageInt = 1 +func (s *ArticleService) DeleteArticleById(id uint64) error { + existingArticle, err := s.repo.GetArticleById(id) + if err != nil { + return errors.New("Artikel Tidak Ditemukan: " + err.Error()) } - total_pages := int(math.Ceil(float64(totalItems) / float64(perPage))) + if existingArticle == nil { + return errors.New("Artikel Tidak Ditemukan: " + err.Error()) + } - if pageInt > total_pages { - pageInt = total_pages + err = s.repo.DeleteArticleById(id) + if err != nil { + return errors.New("Gagal Menghapus Artikel: " + err.Error()) } - return pageInt, total_pages + return nil } -func (s *ArticleService) GetNextPage(currentPage, totalPages int) int { - if currentPage < totalPages { - return currentPage + 1 +func (s *ArticleService) GetAll(page, perPage int) ([]entities.ArticleModels, int64, error) { + articles, err := s.repo.FindAll(page, perPage) + if err != nil { + return nil, 0, errors.New("Artikel Tidak Ditemukan: " + err.Error()) } - return totalPages -} -func (s *ArticleService) GetPrevPage(currentPage int) int { - if currentPage > 1 { - return currentPage - 1 + totalItems, err := s.repo.GetTotalArticleCount() + if err != nil { + return nil, 0, errors.New("Gagal Menghitung Total Artikel: " + err.Error()) } - return 1 + + return articles, totalItems, nil } func (s *ArticleService) GetArticlesByTitle(page, perPage int, title string) ([]entities.ArticleModels, int64, error) { articles, err := s.repo.FindByTitle(page, perPage, title) if err != nil { - return articles, 0, err + return nil, 0, errors.New("Artikel Tidak Ditemukan: " + err.Error()) } totalItems, err := s.repo.GetTotalArticleCountByTitle(title) if err != nil { - return articles, 0, err + return nil, 0, errors.New("Gagal Menghitung Total Artikel: " + err.Error()) } return articles, totalItems, nil } -func (s *ArticleService) UpdateArticleById(id uint64, updatedArticle *entities.ArticleModels) (*entities.ArticleModels, error) { - existingArticle, err := s.repo.GetArticleById(id) +func (s *ArticleService) GetArticleById(id uint64, incrementViews bool) (*entities.ArticleModels, error) { + result, err := s.repo.GetArticleById(id) if err != nil { - return nil, errors.New("artikel tidak ditemukan") + return nil, errors.New("Artikel Tidak Ditemukan: " + err.Error()) } - if existingArticle == nil { - return nil, errors.New("artikel tidak ditemukan") + if incrementViews { + result.Views++ + if err := s.repo.UpdateArticleViews(result); err != nil { + return nil, errors.New("Gagal meningkatkan jumlah tayangan artikel: " + err.Error()) + } } - updatedArticle, err = s.repo.UpdateArticleById(id, updatedArticle) + return result, nil +} + +func (s *ArticleService) GetArticlesByDateRange(page, perPage int, filterType string) ([]entities.ArticleModels, int64, error) { + now := time.Now() + var startDate, endDate time.Time + + switch filterType { + case "today": + startDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) + endDate = startDate.Add(24 * time.Hour) + case "this_week": + startOfWeek := now.AddDate(0, 0, -int(now.Weekday())) + startDate = time.Date(startOfWeek.Year(), startOfWeek.Month(), startOfWeek.Day(), 0, 0, 0, 0, time.UTC) + endDate = startDate.AddDate(0, 0, 7) + case "this_month": + startDate = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) + nextMonth := startDate.AddDate(0, 1, 0) + endDate = nextMonth.Add(-time.Second) + case "this_year": + startDate = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) + nextYear := startDate.AddDate(1, 0, 0) + endDate = nextYear.Add(-time.Second) + default: + return nil, 0, errors.New("Invalid filter type") + } + + result, err := s.repo.GetArticlesByDateRange(page, perPage, startDate, endDate) if err != nil { - return nil, errors.New("gagal mengubah artikel ") + return nil, 0, errors.New("Artikel Tidak Ditemukan: " + err.Error()) } - return updatedArticle, nil + totalItems, err := s.repo.GetTotalArticleCountByDateRange(startDate, endDate) + if err != nil { + return nil, 0, errors.New("Gagal Menghitung Total Artikel: " + err.Error()) + } + + return result, totalItems, nil } -func (s *ArticleService) DeleteArticleById(id uint64) error { - existingArticle, err := s.repo.GetArticleById(id) - if err!= nil { - return errors.New("artikel tidak ditemukan") - } +func (s *ArticleService) CalculatePaginationValues(page int, totalItems int, perPage int) (int, int) { + pageInt := page + if pageInt <= 0 { + pageInt = 1 + } - if existingArticle == nil { - return errors.New("artikel tidak ditemukan") - } + total_pages := int(math.Ceil(float64(totalItems) / float64(perPage))) - err = s.repo.DeleteArticleById(id) - if err!= nil { - return err - } + if pageInt > total_pages { + pageInt = total_pages + } - return nil + return pageInt, total_pages } -func (s *ArticleService) GetArticleById(id uint64) (*entities.ArticleModels, error) { - result, err := s.repo.GetArticleById(id) - if err!= nil { - return nil, errors.New("artikel tidak ditemukan") - } - return result, nil -} \ No newline at end of file +func (s *ArticleService) GetNextPage(currentPage, totalPages int) int { + if currentPage < totalPages { + return currentPage + 1 + } + return totalPages +} + +func (s *ArticleService) GetPrevPage(currentPage int) int { + if currentPage > 1 { + return currentPage - 1 + } + return 1 +} diff --git a/routes/routes.go b/routes/routes.go index c588551..5c39afe 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -63,6 +63,7 @@ func RouteArticle(e *echo.Echo, h article.HandlerArticleInterface, jwtService ut articlesGroup := e.Group("api/v1/articles") articlesGroup.POST("", h.CreateArticle(), middlewares.AuthMiddleware(jwtService, userService)) articlesGroup.GET("", h.GetAllArticles()) + articlesGroup.GET("/:id", h.GetArticleById(), middlewares.AuthMiddleware(jwtService, userService)) articlesGroup.PUT("/:id", h.UpdateArticleById(), middlewares.AuthMiddleware(jwtService, userService)) articlesGroup.DELETE("/:id", h.DeleteArticleById(), middlewares.AuthMiddleware(jwtService, userService)) } From 52ded5023862f31739c593a5a1e9023eb11ff51d Mon Sep 17 00:00:00 2001 From: nurulalyh Date: Fri, 24 Nov 2023 23:37:30 +0800 Subject: [PATCH 2/2] feature(article): get article by id, manage article views, filter article by date range --- module/feature/article/handler/handler.go | 8 ++++---- module/feature/article/service/service.go | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/module/feature/article/handler/handler.go b/module/feature/article/handler/handler.go index 2c40a14..19da837 100644 --- a/module/feature/article/handler/handler.go +++ b/module/feature/article/handler/handler.go @@ -161,22 +161,22 @@ func (h *ArticleHandler) GetAllArticles() echo.HandlerFunc { if search != "" { articles, totalItems, err = h.service.GetArticlesByTitle(page, perPage, search) if err != nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: " + err.Error()) } } else if filterType != "" { articles, totalItems, err = h.service.GetArticlesByDateRange(page, perPage, filterType) if err != nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: " + err.Error()) } } else { articles, totalItems, err = h.service.GetAll(pageConv, perPage) if err != nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: " + err.Error()) } } if err != nil { - return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: "+err.Error()) + return response.SendErrorResponse(c, http.StatusInternalServerError, "Internal Server Error: " + err.Error()) } current_page, total_pages := h.service.CalculatePaginationValues(pageConv, int(totalItems), perPage) diff --git a/module/feature/article/service/service.go b/module/feature/article/service/service.go index 9e936f7..5323bd7 100644 --- a/module/feature/article/service/service.go +++ b/module/feature/article/service/service.go @@ -121,23 +121,23 @@ func (s *ArticleService) GetArticlesByDateRange(page, perPage int, filterType st var startDate, endDate time.Time switch filterType { - case "today": + case "Hari Ini": startDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) endDate = startDate.Add(24 * time.Hour) - case "this_week": + case "Minggu Ini": startOfWeek := now.AddDate(0, 0, -int(now.Weekday())) startDate = time.Date(startOfWeek.Year(), startOfWeek.Month(), startOfWeek.Day(), 0, 0, 0, 0, time.UTC) endDate = startDate.AddDate(0, 0, 7) - case "this_month": + case "Bulan Ini": startDate = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) nextMonth := startDate.AddDate(0, 1, 0) endDate = nextMonth.Add(-time.Second) - case "this_year": + case "Tahun Ini": startDate = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) nextYear := startDate.AddDate(1, 0, 0) endDate = nextYear.Add(-time.Second) default: - return nil, 0, errors.New("Invalid filter type") + return nil, 0, errors.New("Tipe filter tidak valid") } result, err := s.repo.GetArticlesByDateRange(page, perPage, startDate, endDate)