fix: fix validation input fields in point feature

This commit is contained in:
pahmiudahgede 2025-01-26 07:58:40 +07:00
parent b6f5984eba
commit db99c1a441
10 changed files with 381 additions and 453 deletions

View File

@ -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 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 { type ArticleResponse struct {
ID string `json:"id"` ID string `json:"id"`
Title string `json:"title"` Title string `json:"title"`
CoverImage string `json:"coverImage"` CoverImage string `json:"coverImage"`
Author string `json:"author"` Author string `json:"author"`
Heading string `json:"heading"` Heading string `json:"heading"`
Content string `json:"content"` Content string `json:"content"`
PublishedAt time.Time `json:"publishedAt"` PublishedAt string `json:"publishedAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt string `json:"updatedAt"`
PublishedAtFormatted string `json:"publishedAtt"`
UpdatedAtFormatted string `json:"updatedAtt"`
} }
type FormattedResponse struct { type ArticleCreateRequest 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"` Title string `json:"title" validate:"required"`
CoverImage string `json:"coverImage" validate:"required"` CoverImage string `json:"coverImage" validate:"required"`
Author string `json:"author" validate:"required"` Author string `json:"author" validate:"required"`
@ -46,48 +113,10 @@ type ArticleUpdateRequest struct {
Content string `json:"content" validate:"required"` Content string `json:"content" validate:"required"`
} }
func (c *ArticleRequest) ValidatePostArticle() error { type ArticleUpdateRequest struct {
err := validate.Struct(c) Title *string `json:"title,omitempty" validate:"omitempty,min=1"`
if err != nil { CoverImage *string `json:"coverImage,omitempty" validate:"omitempty,url"`
Author *string `json:"author,omitempty" validate:"omitempty,min=1"`
for _, e := range err.(validator.ValidationErrors) { Heading *string `json:"heading,omitempty" validate:"omitempty,min=1"`
Content *string `json:"content,omitempty" validate:"omitempty,min=1"`
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
} }

View File

@ -14,6 +14,6 @@ type BannerCreateRequest struct {
} }
type BannerUpdateRequest struct { type BannerUpdateRequest struct {
BannerName string `json:"bannername,omitempty"` BannerName *string `json:"bannername,omitempty"`
BannerImage string `json:"bannerimage,omitempty"` BannerImage *string `json:"bannerimage,omitempty"`
} }

View File

@ -14,6 +14,16 @@ type PointCreateRequest struct {
} }
type PointUpdateRequest struct { type PointUpdateRequest struct {
CoinName string `json:"coin_name,omitempty"` CoinName string `json:"coin_name" validate:"required"`
ValuePerUnit float64 `json:"value_perunit,omitempty"` 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)
}

View File

@ -19,6 +19,10 @@ func AppRouter(app *fiber.App) {
bannerService := services.NewBannerService(bannerRepo) bannerService := services.NewBannerService(bannerRepo)
bannerController := controllers.NewBannerController(bannerService) bannerController := controllers.NewBannerController(bannerService)
articleRepo := repositories.NewArticleRepository()
articleService := services.NewArticleService(articleRepo)
articleController := controllers.NewArticleController(articleService)
// # api group domain endpoint # // # api group domain endpoint #
api := app.Group("/apirijikid") api := app.Group("/apirijikid")
@ -81,11 +85,11 @@ func AppRouter(app *fiber.App) {
api.Delete("/address/delete-address/:id", middleware.AuthMiddleware, controllers.DeleteAddress) api.Delete("/address/delete-address/:id", middleware.AuthMiddleware, controllers.DeleteAddress)
// # article # // # article #
api.Get("/articles", controllers.GetArticles) api.Get("/articles", articleController.GetAllArticles)
api.Get("/article/:id", controllers.GetArticleByID) api.Get("/article/:id", articleController.GetArticleByID)
api.Post("/article/create-article", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateArticle) api.Post("/article/create-article", middleware.RoleRequired(utils.RoleAdministrator), articleController.CreateArticle)
api.Put("/article/update-article/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateArticle) api.Put("/article/update-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.UpdateArticle)
api.Delete("/article/delete-article/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteArticle) api.Delete("/article/delete-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.DeleteArticle)
// # trash type # // # trash type #
api.Get("/trash-categorys", controllers.GetTrashCategories) api.Get("/trash-categorys", controllers.GetTrashCategories)

View File

@ -7,214 +7,122 @@ import (
"github.com/pahmiudahgede/senggoldong/utils" "github.com/pahmiudahgede/senggoldong/utils"
) )
func CreateArticle(c *fiber.Ctx) error { type ArticleController struct {
var articleRequest dto.ArticleRequest service *services.ArticleService
}
if err := c.BodyParser(&articleRequest); err != nil { func NewArticleController(service *services.ArticleService) *ArticleController {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( return &ArticleController{service: service}
fiber.StatusBadRequest, }
"Input tidak valid",
nil,
))
}
if err := articleRequest.ValidatePostArticle(); err != nil { func (ac *ArticleController) GetAllArticles(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( articles, err := ac.service.GetAllArticles()
fiber.StatusBadRequest,
err.Error(),
nil,
))
}
articleResponse, err := services.CreateArticle(&articleRequest)
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Gagal membuat artikel", "Failed to fetch articles",
nil, ))
}
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) article, err := ac.service.CreateArticle(&request)
articleResponse.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(articleResponse.UpdatedAt) if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
response := dto.FormattedResponse{ fiber.StatusInternalServerError,
ID: articleResponse.ID, err.Error(),
Title: articleResponse.Title, ))
CoverImage: articleResponse.CoverImage,
Author: articleResponse.Author,
Heading: articleResponse.Heading,
Content: articleResponse.Content,
PublishedAtFormatted: articleResponse.PublishedAtFormatted,
UpdatedAtFormatted: articleResponse.UpdatedAtFormatted,
} }
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse( return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
fiber.StatusCreated, fiber.StatusCreated,
"Artikel berhasil dibuat", "Article created successfully",
response, 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 { 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, 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(), 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( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Artikel berhasil diperbarui", "Article updated successfully",
response, article,
)) ))
} }
func DeleteArticle(c *fiber.Ctx) error { func (ac *ArticleController) DeleteArticle(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
err := services.DeleteArticle(id) err := ac.service.DeleteArticle(id)
if err != nil { if err != nil {
if err.Error() == "article not found" {
if err.Error() == "record not found" { return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
fiber.StatusNotFound, fiber.StatusNotFound,
"id article tidak diketahui", "Article not found",
nil,
)) ))
} }
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Gagal menghapus artikel", err.Error(),
nil,
)) ))
} }
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Artikel berhasil dihapus", "Article deleted successfully",
nil, nil,
)) ))
} }

View File

@ -58,8 +58,9 @@ func (pc *PointController) CreatePoint(c *fiber.Ctx) error {
point, err := pc.service.CreatePoint(&request) point, err := pc.service.CreatePoint(&request)
if err != nil { 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(), err.Error(),
)) ))
} }
@ -84,14 +85,8 @@ func (pc *PointController) UpdatePoint(c *fiber.Ctx) error {
point, err := pc.service.UpdatePoint(id, &request) point, err := pc.service.UpdatePoint(id, &request)
if err != nil { if err != nil {
if err.Error() == "point not found" { return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse( fiber.StatusBadRequest,
fiber.StatusNotFound,
"Point not found",
))
}
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
fiber.StatusInternalServerError,
err.Error(), err.Error(),
)) ))
} }
@ -125,4 +120,4 @@ func (pc *PointController) DeletePoint(c *fiber.Ctx) error {
"Point deleted successfully", "Point deleted successfully",
nil, nil,
)) ))
} }

View File

@ -1,99 +1,44 @@
package repositories package repositories
import ( import (
"encoding/json" "errors"
"time"
"github.com/pahmiudahgede/senggoldong/config" "github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/domain"
"golang.org/x/net/context"
) )
var ctx = context.Background() type ArticleRepository struct{}
func CreateArticle(article *domain.Article) error { func NewArticleRepository() *ArticleRepository {
err := config.DB.Create(article).Error return &ArticleRepository{}
if err != nil {
return err
}
config.RedisClient.Del(ctx, "articles")
return nil
} }
func GetArticles() ([]domain.Article, error) { func (r *ArticleRepository) GetAll() ([]domain.Article, error) {
var articles []domain.Article var articles []domain.Article
err := config.DB.Find(&articles).Error
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
if err != nil { 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 return articles, nil
} }
func GetArticleByID(id string) (domain.Article, error) { func (r *ArticleRepository) GetByID(id string) (*domain.Article, error) {
var article domain.Article var article domain.Article
err := config.DB.First(&article, "id = ?", id).Error
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
if err != nil { if err != nil {
return article, err return nil, errors.New("article not found")
} }
return &article, nil
articleJSON, _ := json.Marshal(article)
config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute)
return article, nil
} }
func UpdateArticle(article *domain.Article) error { func (r *ArticleRepository) Create(article *domain.Article) error {
err := config.DB.Save(article).Error return config.DB.Create(article).Error
if err != nil {
return err
}
config.RedisClient.Del(ctx, "article:"+article.ID)
config.RedisClient.Del(ctx, "articles")
return nil
} }
func DeleteArticle(id string) error { func (r *ArticleRepository) Update(article *domain.Article) error {
var article domain.Article return config.DB.Save(article).Error
}
err := config.DB.Where("id = ?", id).First(&article).Error func (r *ArticleRepository) Delete(article *domain.Article) error {
if err != nil { return config.DB.Delete(article).Error
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
} }

View File

@ -1,162 +1,199 @@
package services package services
import ( import (
"context"
"encoding/json" "encoding/json"
"errors"
"time" "time"
"github.com/pahmiudahgede/senggoldong/config" "github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/dto" "github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories" "github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/utils"
) )
var ctx = context.Background() type ArticleService struct {
repo *repositories.ArticleRepository
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
} }
func GetArticles() ([]dto.ArticleResponse, error) { func NewArticleService(repo *repositories.ArticleRepository) *ArticleService {
var response []dto.ArticleResponse return &ArticleService{repo: repo}
}
cachedArticles, err := config.RedisClient.Get(ctx, "articles").Result() func (s *ArticleService) GetAllArticles() ([]dto.ArticleResponse, error) {
if err == nil { ctx := config.Context()
err := json.Unmarshal([]byte(cachedArticles), &response) cacheKey := "articles:all"
if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
var result []dto.ArticleResponse
for _, article := range articles { for _, article := range articles {
response = append(response, dto.ArticleResponse{ result = append(result, dto.ArticleResponse{
ID: article.ID, ID: article.ID,
Title: article.Title, Title: article.Title,
CoverImage: article.CoverImage, CoverImage: article.CoverImage,
Author: article.Author, Author: article.Author,
Heading: article.Heading, Heading: article.Heading,
Content: article.Content, Content: article.Content,
PublishedAt: article.PublishedAt, PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt),
UpdatedAt: article.UpdatedAt, UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt),
}) })
} }
articlesJSON, _ := json.Marshal(response) cacheData, _ := json.Marshal(result)
config.RedisClient.Set(ctx, "articles", articlesJSON, 10*time.Minute) 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 return response, nil
} }
func GetArticleByID(id string) (dto.ArticleResponse, error) { func (s *ArticleService) UpdateArticle(id string, request *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) {
cachedArticle, err := config.RedisClient.Get(ctx, "article:"+id).Result()
if err == nil { if err := dto.GetValidator().Struct(request); err != nil {
var article dto.ArticleResponse return nil, errors.New("invalid input data")
err := json.Unmarshal([]byte(cachedArticle), &article)
if err != nil {
return article, err
}
return article, nil
} }
article, err := repositories.GetArticleByID(id) article, err := s.repo.GetByID(id)
if err != nil { if err != nil {
return dto.ArticleResponse{}, err return nil, errors.New("article not found")
} }
articleResponse := dto.ArticleResponse{ if request.Title != nil {
ID: article.ID, article.Title = *request.Title
Title: article.Title,
CoverImage: article.CoverImage,
Author: article.Author,
Heading: article.Heading,
Content: article.Content,
PublishedAt: article.PublishedAt,
UpdatedAt: article.UpdatedAt,
} }
if request.CoverImage != nil {
articleJSON, _ := json.Marshal(articleResponse) article.CoverImage = *request.CoverImage
config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute) }
if request.Author != nil {
return articleResponse, nil article.Author = *request.Author
} }
if request.Heading != nil {
func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) { article.Heading = *request.Heading
}
article, err := repositories.GetArticleByID(id) if request.Content != nil {
if err != nil { article.Content = *request.Content
return nil, err
} }
article.Title = articleUpdateRequest.Title
article.CoverImage = articleUpdateRequest.CoverImage
article.Author = articleUpdateRequest.Author
article.Heading = articleUpdateRequest.Heading
article.Content = articleUpdateRequest.Content
article.UpdatedAt = time.Now() article.UpdatedAt = time.Now()
err = repositories.UpdateArticle(&article) err = s.repo.Update(article)
if err != nil { if err != nil {
return nil, err return nil, errors.New("failed to update article")
} }
config.RedisClient.Del(ctx, "article:"+id) ctx := config.Context()
config.RedisClient.Del(ctx, "articles") config.RedisClient.Del(ctx, "articles:all")
config.RedisClient.Del(ctx, "articles:"+id)
updatedArticleResponse := dto.ArticleResponse{ response := &dto.ArticleResponse{
ID: article.ID, ID: article.ID,
Title: article.Title, Title: article.Title,
CoverImage: article.CoverImage, CoverImage: article.CoverImage,
Author: article.Author, Author: article.Author,
Heading: article.Heading, Heading: article.Heading,
Content: article.Content, Content: article.Content,
PublishedAt: article.PublishedAt, PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt),
UpdatedAt: article.UpdatedAt, 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 { if err != nil {
return err return errors.New("article not found")
} }
config.RedisClient.Del(ctx, "article:"+id) err = s.repo.Delete(article)
config.RedisClient.Del(ctx, "articles") 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 return nil
} }

View File

@ -122,11 +122,11 @@ func (s *BannerService) UpdateBanner(id string, request *dto.BannerUpdateRequest
return nil, errors.New("banner not found") return nil, errors.New("banner not found")
} }
if request.BannerName != "" { if request.BannerName != nil && *request.BannerName != "" {
banner.BannerName = request.BannerName banner.BannerName = *request.BannerName
} }
if request.BannerImage != "" { if request.BannerImage != nil && *request.BannerImage != "" {
banner.BannerImage = request.BannerImage banner.BannerImage = *request.BannerImage
} }
banner.UpdatedAt = time.Now() banner.UpdatedAt = time.Now()

View File

@ -87,8 +87,8 @@ func (s *PointService) GetPointByID(id string) (*dto.PointResponse, error) {
func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointResponse, error) { func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointResponse, error) {
if request.CoinName == "" || request.ValuePerUnit <= 0 { if err := request.Validate(); err != nil {
return nil, errors.New("invalid input data") return nil, err
} }
newPoint := &domain.Point{ 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) { 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) point, err := s.repo.GetByID(id)
if err != nil { if err != nil {
return nil, errors.New("point not found") return nil, errors.New("point not found")
} }
if request.CoinName != "" { point.CoinName = request.CoinName
point.CoinName = request.CoinName point.ValuePerUnit = request.ValuePerUnit
}
if request.ValuePerUnit > 0 {
point.ValuePerUnit = request.ValuePerUnit
}
point.UpdatedAt = time.Now() point.UpdatedAt = time.Now()
err = s.repo.Update(point) err = s.repo.Update(point)