diff --git a/dto/article.go b/dto/article.go index 28bd171..94bc738 100644 --- a/dto/article.go +++ b/dto/article.go @@ -1,44 +1,111 @@ +// package dto + +// import ( +// "fmt" +// "time" + +// "github.com/go-playground/validator/v10" +// ) + +// type ArticleRequest struct { +// Title string `json:"title" validate:"required"` +// CoverImage string `json:"coverImage" validate:"required"` +// Author string `json:"author" validate:"required"` +// Heading string `json:"heading" validate:"required"` +// Content string `json:"content" validate:"required"` +// } + +// type ArticleResponse struct { +// ID string `json:"id"` +// Title string `json:"title"` +// CoverImage string `json:"coverImage"` +// Author string `json:"author"` +// Heading string `json:"heading"` +// Content string `json:"content"` +// PublishedAt time.Time `json:"publishedAt"` +// UpdatedAt time.Time `json:"updatedAt"` +// PublishedAtFormatted string `json:"publishedAtt"` +// UpdatedAtFormatted string `json:"updatedAtt"` +// } + +// type FormattedResponse struct { +// ID string `json:"id"` +// Title string `json:"title"` +// CoverImage string `json:"coverImage"` +// Author string `json:"author"` +// Heading string `json:"heading"` +// Content string `json:"content"` +// PublishedAtFormatted string `json:"publishedAt"` +// UpdatedAtFormatted string `json:"updatedAt"` +// } +// type ArticleUpdateRequest struct { +// Title string `json:"title" validate:"required"` +// CoverImage string `json:"coverImage" validate:"required"` +// Author string `json:"author" validate:"required"` +// Heading string `json:"heading" validate:"required"` +// Content string `json:"content" validate:"required"` +// } + +// func (c *ArticleRequest) ValidatePostArticle() error { +// err := validate.Struct(c) +// if err != nil { + +// for _, e := range err.(validator.ValidationErrors) { + +// switch e.Field() { +// case "Title": +// return fmt.Errorf("judul harus diisi") +// case "CoverImage": +// return fmt.Errorf("gambar cover harus diisi") +// case "Author": +// return fmt.Errorf("penulis harus diisi") +// case "Heading": +// return fmt.Errorf("heading harus diisi") +// case "Content": +// return fmt.Errorf("konten artikel harus diisi") +// } +// } +// } +// return nil +// } + +// func (c *ArticleUpdateRequest) ValidateUpdateArticle() error { +// err := validate.Struct(c) +// if err != nil { + +// for _, e := range err.(validator.ValidationErrors) { + +// switch e.Field() { +// case "Title": +// return fmt.Errorf("judul harus diisi") +// case "CoverImage": +// return fmt.Errorf("gambar cover harus diisi") +// case "Author": +// return fmt.Errorf("penulis harus diisi") +// case "Heading": +// return fmt.Errorf("heading harus diisi") +// case "Content": +// return fmt.Errorf("konten artikel harus diisi") +// } +// } +// } +// return nil +// } + package dto -import ( - "fmt" - "time" - - "github.com/go-playground/validator/v10" -) - -type ArticleRequest struct { - Title string `json:"title" validate:"required"` - CoverImage string `json:"coverImage" validate:"required"` - Author string `json:"author" validate:"required"` - Heading string `json:"heading" validate:"required"` - Content string `json:"content" validate:"required"` -} - type ArticleResponse struct { - ID string `json:"id"` - Title string `json:"title"` - CoverImage string `json:"coverImage"` - Author string `json:"author"` - Heading string `json:"heading"` - Content string `json:"content"` - PublishedAt time.Time `json:"publishedAt"` - UpdatedAt time.Time `json:"updatedAt"` - PublishedAtFormatted string `json:"publishedAtt"` - UpdatedAtFormatted string `json:"updatedAtt"` + ID string `json:"id"` + Title string `json:"title"` + CoverImage string `json:"coverImage"` + Author string `json:"author"` + Heading string `json:"heading"` + Content string `json:"content"` + PublishedAt string `json:"publishedAt"` + UpdatedAt string `json:"updatedAt"` } -type FormattedResponse struct { - ID string `json:"id"` - Title string `json:"title"` - CoverImage string `json:"coverImage"` - Author string `json:"author"` - Heading string `json:"heading"` - Content string `json:"content"` - PublishedAtFormatted string `json:"publishedAt"` - UpdatedAtFormatted string `json:"updatedAt"` -} -type ArticleUpdateRequest struct { +type ArticleCreateRequest struct { Title string `json:"title" validate:"required"` CoverImage string `json:"coverImage" validate:"required"` Author string `json:"author" validate:"required"` @@ -46,48 +113,10 @@ type ArticleUpdateRequest struct { Content string `json:"content" validate:"required"` } -func (c *ArticleRequest) ValidatePostArticle() error { - err := validate.Struct(c) - if err != nil { - - for _, e := range err.(validator.ValidationErrors) { - - switch e.Field() { - case "Title": - return fmt.Errorf("judul harus diisi") - case "CoverImage": - return fmt.Errorf("gambar cover harus diisi") - case "Author": - return fmt.Errorf("penulis harus diisi") - case "Heading": - return fmt.Errorf("heading harus diisi") - case "Content": - return fmt.Errorf("konten artikel harus diisi") - } - } - } - return nil -} - -func (c *ArticleUpdateRequest) ValidateUpdateArticle() error { - err := validate.Struct(c) - if err != nil { - - for _, e := range err.(validator.ValidationErrors) { - - switch e.Field() { - case "Title": - return fmt.Errorf("judul harus diisi") - case "CoverImage": - return fmt.Errorf("gambar cover harus diisi") - case "Author": - return fmt.Errorf("penulis harus diisi") - case "Heading": - return fmt.Errorf("heading harus diisi") - case "Content": - return fmt.Errorf("konten artikel harus diisi") - } - } - } - return nil +type ArticleUpdateRequest struct { + Title *string `json:"title,omitempty" validate:"omitempty,min=1"` + CoverImage *string `json:"coverImage,omitempty" validate:"omitempty,url"` + Author *string `json:"author,omitempty" validate:"omitempty,min=1"` + Heading *string `json:"heading,omitempty" validate:"omitempty,min=1"` + Content *string `json:"content,omitempty" validate:"omitempty,min=1"` } diff --git a/dto/banner.go b/dto/banner.go index 34cd76a..55fc2d4 100644 --- a/dto/banner.go +++ b/dto/banner.go @@ -14,6 +14,6 @@ type BannerCreateRequest struct { } type BannerUpdateRequest struct { - BannerName string `json:"bannername,omitempty"` - BannerImage string `json:"bannerimage,omitempty"` + BannerName *string `json:"bannername,omitempty"` + BannerImage *string `json:"bannerimage,omitempty"` } diff --git a/dto/initialcoint.go b/dto/initialcoint.go index 9c31407..bf59be1 100644 --- a/dto/initialcoint.go +++ b/dto/initialcoint.go @@ -14,6 +14,16 @@ type PointCreateRequest struct { } type PointUpdateRequest struct { - CoinName string `json:"coin_name,omitempty"` - ValuePerUnit float64 `json:"value_perunit,omitempty"` -} \ No newline at end of file + CoinName string `json:"coin_name" validate:"required"` + ValuePerUnit float64 `json:"value_perunit" validate:"required,gt=0"` +} + +func (p *PointCreateRequest) Validate() error { + validate := GetValidator() + return validate.Struct(p) +} + +func (p *PointUpdateRequest) Validate() error { + validate := GetValidator() + return validate.Struct(p) +} diff --git a/internal/api/routes.go b/internal/api/routes.go index e3f8d96..de65c04 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -19,6 +19,10 @@ func AppRouter(app *fiber.App) { bannerService := services.NewBannerService(bannerRepo) bannerController := controllers.NewBannerController(bannerService) + articleRepo := repositories.NewArticleRepository() + articleService := services.NewArticleService(articleRepo) + articleController := controllers.NewArticleController(articleService) + // # api group domain endpoint # api := app.Group("/apirijikid") @@ -81,11 +85,11 @@ func AppRouter(app *fiber.App) { api.Delete("/address/delete-address/:id", middleware.AuthMiddleware, controllers.DeleteAddress) // # article # - api.Get("/articles", controllers.GetArticles) - api.Get("/article/:id", controllers.GetArticleByID) - api.Post("/article/create-article", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateArticle) - api.Put("/article/update-article/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateArticle) - api.Delete("/article/delete-article/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteArticle) + api.Get("/articles", articleController.GetAllArticles) + api.Get("/article/:id", articleController.GetArticleByID) + api.Post("/article/create-article", middleware.RoleRequired(utils.RoleAdministrator), articleController.CreateArticle) + api.Put("/article/update-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.UpdateArticle) + api.Delete("/article/delete-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.DeleteArticle) // # trash type # api.Get("/trash-categorys", controllers.GetTrashCategories) diff --git a/internal/controllers/article.go b/internal/controllers/article.go index 673000f..6583fe8 100644 --- a/internal/controllers/article.go +++ b/internal/controllers/article.go @@ -7,214 +7,122 @@ import ( "github.com/pahmiudahgede/senggoldong/utils" ) -func CreateArticle(c *fiber.Ctx) error { - var articleRequest dto.ArticleRequest +type ArticleController struct { + service *services.ArticleService +} - if err := c.BodyParser(&articleRequest); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( - fiber.StatusBadRequest, - "Input tidak valid", - nil, - )) - } +func NewArticleController(service *services.ArticleService) *ArticleController { + return &ArticleController{service: service} +} - if err := articleRequest.ValidatePostArticle(); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( - fiber.StatusBadRequest, - err.Error(), - nil, - )) - } - - articleResponse, err := services.CreateArticle(&articleRequest) +func (ac *ArticleController) GetAllArticles(c *fiber.Ctx) error { + articles, err := ac.service.GetAllArticles() if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( + return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( fiber.StatusInternalServerError, - "Gagal membuat artikel", - nil, + "Failed to fetch articles", + )) + } + return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( + fiber.StatusOK, + "Articles fetched successfully", + articles, + )) +} + +func (ac *ArticleController) GetArticleByID(c *fiber.Ctx) error { + id := c.Params("id") + article, err := ac.service.GetArticleByID(id) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse( + fiber.StatusNotFound, + "Article not found", + )) + } + return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( + fiber.StatusOK, + "Article fetched successfully", + article, + )) +} + +func (ac *ArticleController) CreateArticle(c *fiber.Ctx) error { + var request dto.ArticleCreateRequest + + if err := c.BodyParser(&request); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse( + fiber.StatusBadRequest, + "Invalid request body", )) } - articleResponse.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(articleResponse.PublishedAt) - articleResponse.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(articleResponse.UpdatedAt) - - response := dto.FormattedResponse{ - ID: articleResponse.ID, - Title: articleResponse.Title, - CoverImage: articleResponse.CoverImage, - Author: articleResponse.Author, - Heading: articleResponse.Heading, - Content: articleResponse.Content, - PublishedAtFormatted: articleResponse.PublishedAtFormatted, - UpdatedAtFormatted: articleResponse.UpdatedAtFormatted, + article, err := ac.service.CreateArticle(&request) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( + fiber.StatusInternalServerError, + err.Error(), + )) } return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse( fiber.StatusCreated, - "Artikel berhasil dibuat", - response, + "Article created successfully", + article, )) } -func GetArticles(c *fiber.Ctx) error { +func (ac *ArticleController) UpdateArticle(c *fiber.Ctx) error { + id := c.Params("id") + var request dto.ArticleUpdateRequest - articles, err := services.GetArticles() + if err := c.BodyParser(&request); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse( + fiber.StatusBadRequest, + "Invalid request body", + )) + } + + article, err := ac.service.UpdateArticle(id, &request) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( + if err.Error() == "article not found" { + return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse( + fiber.StatusNotFound, + "Article not found", + )) + } + return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( fiber.StatusInternalServerError, - "Gagal mengambil artikel", - nil, - )) - } - - if len(articles) == 0 { - return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( - fiber.StatusOK, - "Artikel berhasil diambil tetapi data kosong", - []dto.FormattedResponse{}, - )) - } - - var formattedArticles []dto.FormattedResponse - for _, article := range articles { - article.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(article.PublishedAt) - article.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(article.UpdatedAt) - - formattedArticles = append(formattedArticles, dto.FormattedResponse{ - ID: article.ID, - Title: article.Title, - CoverImage: article.CoverImage, - Author: article.Author, - Heading: article.Heading, - Content: article.Content, - PublishedAtFormatted: article.PublishedAtFormatted, - UpdatedAtFormatted: article.UpdatedAtFormatted, - }) - } - - return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( - fiber.StatusOK, - "Artikel berhasil diambil", - formattedArticles, - )) -} - -func GetArticleByID(c *fiber.Ctx) error { - - id := c.Params("id") - - article, err := services.GetArticleByID(id) - if err != nil { - return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( - fiber.StatusNotFound, - "Artikel tidak ditemukan", - nil, - )) - } - - article.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(article.PublishedAt) - article.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(article.UpdatedAt) - - response := dto.FormattedResponse{ - ID: article.ID, - Title: article.Title, - CoverImage: article.CoverImage, - Author: article.Author, - Heading: article.Heading, - Content: article.Content, - PublishedAtFormatted: article.PublishedAtFormatted, - UpdatedAtFormatted: article.UpdatedAtFormatted, - } - - return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( - fiber.StatusOK, - "Artikel berhasil diambil", - response, - )) -} - -func UpdateArticle(c *fiber.Ctx) error { - id := c.Params("id") - - var articleUpdateRequest dto.ArticleUpdateRequest - - if err := c.BodyParser(&articleUpdateRequest); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( - fiber.StatusBadRequest, - "Input tidak valid", - nil, - )) - } - - if err := articleUpdateRequest.ValidateUpdateArticle(); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( - fiber.StatusBadRequest, err.Error(), - nil, )) } - updatedArticle, err := services.UpdateArticle(id, &articleUpdateRequest) - if err != nil { - - if err.Error() == "record not found" { - return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( - fiber.StatusNotFound, - "id article tidak diketahui", - nil, - )) - } - return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( - fiber.StatusInternalServerError, - "Gagal memperbarui artikel", - nil, - )) - } - - updatedArticle.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(updatedArticle.PublishedAt) - updatedArticle.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(updatedArticle.UpdatedAt) - - response := dto.FormattedResponse{ - ID: updatedArticle.ID, - Title: updatedArticle.Title, - CoverImage: updatedArticle.CoverImage, - Author: updatedArticle.Author, - Heading: updatedArticle.Heading, - Content: updatedArticle.Content, - PublishedAtFormatted: updatedArticle.PublishedAtFormatted, - UpdatedAtFormatted: updatedArticle.UpdatedAtFormatted, - } - return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( fiber.StatusOK, - "Artikel berhasil diperbarui", - response, + "Article updated successfully", + article, )) } -func DeleteArticle(c *fiber.Ctx) error { +func (ac *ArticleController) DeleteArticle(c *fiber.Ctx) error { id := c.Params("id") - err := services.DeleteArticle(id) + err := ac.service.DeleteArticle(id) if err != nil { - - if err.Error() == "record not found" { - return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( + if err.Error() == "article not found" { + return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse( fiber.StatusNotFound, - "id article tidak diketahui", - nil, + "Article not found", )) } - return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( + return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( fiber.StatusInternalServerError, - "Gagal menghapus artikel", - nil, + err.Error(), )) } return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( fiber.StatusOK, - "Artikel berhasil dihapus", + "Article deleted successfully", nil, )) -} +} \ No newline at end of file diff --git a/internal/controllers/initialcoint.go b/internal/controllers/initialcoint.go index 82f7ea4..f7e1bee 100644 --- a/internal/controllers/initialcoint.go +++ b/internal/controllers/initialcoint.go @@ -58,8 +58,9 @@ func (pc *PointController) CreatePoint(c *fiber.Ctx) error { point, err := pc.service.CreatePoint(&request) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( - fiber.StatusInternalServerError, + + return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse( + fiber.StatusBadRequest, err.Error(), )) } @@ -84,14 +85,8 @@ func (pc *PointController) UpdatePoint(c *fiber.Ctx) error { point, err := pc.service.UpdatePoint(id, &request) if err != nil { - if err.Error() == "point not found" { - return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse( - fiber.StatusNotFound, - "Point not found", - )) - } - return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse( - fiber.StatusInternalServerError, + return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse( + fiber.StatusBadRequest, err.Error(), )) } @@ -125,4 +120,4 @@ func (pc *PointController) DeletePoint(c *fiber.Ctx) error { "Point deleted successfully", nil, )) -} \ No newline at end of file +} diff --git a/internal/repositories/article.go b/internal/repositories/article.go index fba1b4e..7ef8822 100644 --- a/internal/repositories/article.go +++ b/internal/repositories/article.go @@ -1,99 +1,44 @@ package repositories import ( - "encoding/json" - "time" + "errors" "github.com/pahmiudahgede/senggoldong/config" "github.com/pahmiudahgede/senggoldong/domain" - "golang.org/x/net/context" ) -var ctx = context.Background() +type ArticleRepository struct{} -func CreateArticle(article *domain.Article) error { - err := config.DB.Create(article).Error - if err != nil { - return err - } - - config.RedisClient.Del(ctx, "articles") - return nil +func NewArticleRepository() *ArticleRepository { + return &ArticleRepository{} } -func GetArticles() ([]domain.Article, error) { +func (r *ArticleRepository) GetAll() ([]domain.Article, error) { var articles []domain.Article - - cachedArticles, err := config.RedisClient.Get(ctx, "articles").Result() - if err == nil { - err := json.Unmarshal([]byte(cachedArticles), &articles) - if err != nil { - return nil, err - } - return articles, nil - } - - err = config.DB.Find(&articles).Error + err := config.DB.Find(&articles).Error if err != nil { - return nil, err + return nil, errors.New("failed to fetch articles from database") } - - articlesJSON, _ := json.Marshal(articles) - config.RedisClient.Set(ctx, "articles", articlesJSON, 10*time.Minute) - return articles, nil } -func GetArticleByID(id string) (domain.Article, error) { +func (r *ArticleRepository) GetByID(id string) (*domain.Article, error) { var article domain.Article - - cachedArticle, err := config.RedisClient.Get(ctx, "article:"+id).Result() - if err == nil { - err := json.Unmarshal([]byte(cachedArticle), &article) - if err != nil { - return article, err - } - return article, nil - } - - err = config.DB.Where("id = ?", id).First(&article).Error + err := config.DB.First(&article, "id = ?", id).Error if err != nil { - return article, err + return nil, errors.New("article not found") } - - articleJSON, _ := json.Marshal(article) - config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute) - - return article, nil + return &article, nil } -func UpdateArticle(article *domain.Article) error { - err := config.DB.Save(article).Error - if err != nil { - return err - } - - config.RedisClient.Del(ctx, "article:"+article.ID) - config.RedisClient.Del(ctx, "articles") - - return nil +func (r *ArticleRepository) Create(article *domain.Article) error { + return config.DB.Create(article).Error } -func DeleteArticle(id string) error { - var article domain.Article +func (r *ArticleRepository) Update(article *domain.Article) error { + return config.DB.Save(article).Error +} - err := config.DB.Where("id = ?", id).First(&article).Error - if err != nil { - return err - } - - err = config.DB.Delete(&article).Error - if err != nil { - return err - } - - config.RedisClient.Del(ctx, "article:"+id) - config.RedisClient.Del(ctx, "articles") - - return nil +func (r *ArticleRepository) Delete(article *domain.Article) error { + return config.DB.Delete(article).Error } \ No newline at end of file diff --git a/internal/services/article.go b/internal/services/article.go index 9d2a77c..5e9093f 100644 --- a/internal/services/article.go +++ b/internal/services/article.go @@ -1,162 +1,199 @@ package services import ( - "context" "encoding/json" + "errors" "time" "github.com/pahmiudahgede/senggoldong/config" "github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/dto" "github.com/pahmiudahgede/senggoldong/internal/repositories" + "github.com/pahmiudahgede/senggoldong/utils" ) -var ctx = context.Background() - -func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, error) { - article := domain.Article{ - Title: articleRequest.Title, - CoverImage: articleRequest.CoverImage, - Author: articleRequest.Author, - Heading: articleRequest.Heading, - Content: articleRequest.Content, - } - - err := repositories.CreateArticle(&article) - if err != nil { - return dto.ArticleResponse{}, err - } - - config.RedisClient.Del(ctx, "articles") - - articleResponse := dto.ArticleResponse{ - ID: article.ID, - Title: article.Title, - CoverImage: article.CoverImage, - Author: article.Author, - Heading: article.Heading, - Content: article.Content, - PublishedAt: article.PublishedAt, - UpdatedAt: article.UpdatedAt, - } - - return articleResponse, nil +type ArticleService struct { + repo *repositories.ArticleRepository } -func GetArticles() ([]dto.ArticleResponse, error) { - var response []dto.ArticleResponse +func NewArticleService(repo *repositories.ArticleRepository) *ArticleService { + return &ArticleService{repo: repo} +} - cachedArticles, err := config.RedisClient.Get(ctx, "articles").Result() - if err == nil { - err := json.Unmarshal([]byte(cachedArticles), &response) - if err != nil { - return nil, err +func (s *ArticleService) GetAllArticles() ([]dto.ArticleResponse, error) { + ctx := config.Context() + cacheKey := "articles:all" + + cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result() + if err == nil && cachedData != "" { + var cachedArticles []dto.ArticleResponse + if err := json.Unmarshal([]byte(cachedData), &cachedArticles); err == nil { + return cachedArticles, nil } - return response, nil } - articles, err := repositories.GetArticles() + articles, err := s.repo.GetAll() if err != nil { return nil, err } + var result []dto.ArticleResponse for _, article := range articles { - response = append(response, dto.ArticleResponse{ + result = append(result, dto.ArticleResponse{ ID: article.ID, Title: article.Title, CoverImage: article.CoverImage, Author: article.Author, Heading: article.Heading, Content: article.Content, - PublishedAt: article.PublishedAt, - UpdatedAt: article.UpdatedAt, + PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt), }) } - articlesJSON, _ := json.Marshal(response) - config.RedisClient.Set(ctx, "articles", articlesJSON, 10*time.Minute) + cacheData, _ := json.Marshal(result) + config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5) + + return result, nil +} + +func (s *ArticleService) GetArticleByID(id string) (*dto.ArticleResponse, error) { + ctx := config.Context() + cacheKey := "articles:" + id + + cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result() + if err == nil && cachedData != "" { + var cachedArticle dto.ArticleResponse + if err := json.Unmarshal([]byte(cachedData), &cachedArticle); err == nil { + return &cachedArticle, nil + } + } + + article, err := s.repo.GetByID(id) + if err != nil { + return nil, err + } + + result := &dto.ArticleResponse{ + ID: article.ID, + Title: article.Title, + CoverImage: article.CoverImage, + Author: article.Author, + Heading: article.Heading, + Content: article.Content, + PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt), + } + + cacheData, _ := json.Marshal(result) + config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5) + + return result, nil +} + +func (s *ArticleService) CreateArticle(request *dto.ArticleCreateRequest) (*dto.ArticleResponse, error) { + + if request.Title == "" || request.CoverImage == "" || request.Author == "" || + request.Heading == "" || request.Content == "" { + return nil, errors.New("invalid input data") + } + + newArticle := &domain.Article{ + Title: request.Title, + CoverImage: request.CoverImage, + Author: request.Author, + Heading: request.Heading, + Content: request.Content, + } + + err := s.repo.Create(newArticle) + if err != nil { + return nil, errors.New("failed to create article") + } + + ctx := config.Context() + config.RedisClient.Del(ctx, "articles:all") + + response := &dto.ArticleResponse{ + ID: newArticle.ID, + Title: newArticle.Title, + CoverImage: newArticle.CoverImage, + Author: newArticle.Author, + Heading: newArticle.Heading, + Content: newArticle.Content, + PublishedAt: utils.FormatDateToIndonesianFormat(newArticle.PublishedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(newArticle.UpdatedAt), + } return response, nil } -func GetArticleByID(id string) (dto.ArticleResponse, error) { - cachedArticle, err := config.RedisClient.Get(ctx, "article:"+id).Result() - if err == nil { - var article dto.ArticleResponse - err := json.Unmarshal([]byte(cachedArticle), &article) - if err != nil { - return article, err - } - return article, nil +func (s *ArticleService) UpdateArticle(id string, request *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) { + + if err := dto.GetValidator().Struct(request); err != nil { + return nil, errors.New("invalid input data") } - article, err := repositories.GetArticleByID(id) + article, err := s.repo.GetByID(id) if err != nil { - return dto.ArticleResponse{}, err + return nil, errors.New("article not found") } - articleResponse := dto.ArticleResponse{ - ID: article.ID, - Title: article.Title, - CoverImage: article.CoverImage, - Author: article.Author, - Heading: article.Heading, - Content: article.Content, - PublishedAt: article.PublishedAt, - UpdatedAt: article.UpdatedAt, + if request.Title != nil { + article.Title = *request.Title } - - articleJSON, _ := json.Marshal(articleResponse) - config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute) - - return articleResponse, nil -} - -func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) { - - article, err := repositories.GetArticleByID(id) - if err != nil { - return nil, err + if request.CoverImage != nil { + article.CoverImage = *request.CoverImage + } + if request.Author != nil { + article.Author = *request.Author + } + if request.Heading != nil { + article.Heading = *request.Heading + } + if request.Content != nil { + article.Content = *request.Content } - - article.Title = articleUpdateRequest.Title - article.CoverImage = articleUpdateRequest.CoverImage - article.Author = articleUpdateRequest.Author - article.Heading = articleUpdateRequest.Heading - article.Content = articleUpdateRequest.Content article.UpdatedAt = time.Now() - err = repositories.UpdateArticle(&article) + err = s.repo.Update(article) if err != nil { - return nil, err + return nil, errors.New("failed to update article") } - config.RedisClient.Del(ctx, "article:"+id) - config.RedisClient.Del(ctx, "articles") + ctx := config.Context() + config.RedisClient.Del(ctx, "articles:all") + config.RedisClient.Del(ctx, "articles:"+id) - updatedArticleResponse := dto.ArticleResponse{ + response := &dto.ArticleResponse{ ID: article.ID, Title: article.Title, CoverImage: article.CoverImage, Author: article.Author, Heading: article.Heading, Content: article.Content, - PublishedAt: article.PublishedAt, - UpdatedAt: article.UpdatedAt, + PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt), } - return &updatedArticleResponse, nil + return response, nil } -func DeleteArticle(id string) error { +func (s *ArticleService) DeleteArticle(id string) error { - err := repositories.DeleteArticle(id) + article, err := s.repo.GetByID(id) if err != nil { - return err + return errors.New("article not found") } - config.RedisClient.Del(ctx, "article:"+id) - config.RedisClient.Del(ctx, "articles") + err = s.repo.Delete(article) + if err != nil { + return errors.New("failed to delete article") + } + + ctx := config.Context() + config.RedisClient.Del(ctx, "articles:all") + config.RedisClient.Del(ctx, "articles:"+id) return nil } \ No newline at end of file diff --git a/internal/services/banner.go b/internal/services/banner.go index c10fbb1..fb3125a 100644 --- a/internal/services/banner.go +++ b/internal/services/banner.go @@ -122,11 +122,11 @@ func (s *BannerService) UpdateBanner(id string, request *dto.BannerUpdateRequest return nil, errors.New("banner not found") } - if request.BannerName != "" { - banner.BannerName = request.BannerName + if request.BannerName != nil && *request.BannerName != "" { + banner.BannerName = *request.BannerName } - if request.BannerImage != "" { - banner.BannerImage = request.BannerImage + if request.BannerImage != nil && *request.BannerImage != "" { + banner.BannerImage = *request.BannerImage } banner.UpdatedAt = time.Now() diff --git a/internal/services/initialcoint.go b/internal/services/initialcoint.go index f03881c..13fa8e1 100644 --- a/internal/services/initialcoint.go +++ b/internal/services/initialcoint.go @@ -87,8 +87,8 @@ func (s *PointService) GetPointByID(id string) (*dto.PointResponse, error) { func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointResponse, error) { - if request.CoinName == "" || request.ValuePerUnit <= 0 { - return nil, errors.New("invalid input data") + if err := request.Validate(); err != nil { + return nil, err } newPoint := &domain.Point{ @@ -117,17 +117,17 @@ func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointR func (s *PointService) UpdatePoint(id string, request *dto.PointUpdateRequest) (*dto.PointResponse, error) { + if err := request.Validate(); err != nil { + return nil, err + } + point, err := s.repo.GetByID(id) if err != nil { return nil, errors.New("point not found") } - if request.CoinName != "" { - point.CoinName = request.CoinName - } - if request.ValuePerUnit > 0 { - point.ValuePerUnit = request.ValuePerUnit - } + point.CoinName = request.CoinName + point.ValuePerUnit = request.ValuePerUnit point.UpdatedAt = time.Now() err = s.repo.Update(point)