feat: add feature create article

This commit is contained in:
pahmiudahgede 2025-02-08 02:25:33 +07:00
parent 26cbe82891
commit a9f2aec4ec
8 changed files with 247 additions and 0 deletions

View File

@ -43,6 +43,7 @@ func ConnectDatabase() {
&model.Role{}, &model.Role{},
&model.UserPin{}, &model.UserPin{},
&model.Address{}, &model.Address{},
&model.Article{},
// ==main feature== // ==main feature==
) )
if err != nil { if err != nil {

50
dto/article_dto.go Normal file
View File

@ -0,0 +1,50 @@
package dto
import (
"strings"
)
type ArticleResponseDTO struct {
ID string `json:"article_id"`
Title string `json:"title"`
CoverImage string `json:"coverImage"`
Author string `json:"author"`
Heading string `json:"heading"`
Content string `json:"content"`
PublishedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestArticleDTO struct {
Title string `json:"title"`
CoverImage string `json:"coverImage"`
Author string `json:"author"`
Heading string `json:"heading"`
Content string `json:"content"`
}
func (r *RequestArticleDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Title) == "" {
errors["title"] = append(errors["title"], "Title is required")
}
if strings.TrimSpace(r.CoverImage) == "" {
errors["coverImage"] = append(errors["coverImage"], "Cover image is required")
}
if strings.TrimSpace(r.Author) == "" {
errors["author"] = append(errors["author"], "Author is required")
}
if strings.TrimSpace(r.Heading) == "" {
errors["heading"] = append(errors["heading"], "Heading is required")
}
if strings.TrimSpace(r.Content) == "" {
errors["content"] = append(errors["content"], "Content is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -0,0 +1,35 @@
package handler
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/services"
"github.com/pahmiudahgede/senggoldong/utils"
)
type ArticleHandler struct {
ArticleService services.ArticleService
}
func NewArticleHandler(articleService services.ArticleService) *ArticleHandler {
return &ArticleHandler{ArticleService: articleService}
}
func (h *ArticleHandler) CreateArticle(c *fiber.Ctx) error {
var requestArticleDTO dto.RequestArticleDTO
if err := c.BodyParser(&requestArticleDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := requestArticleDTO.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
articleResponse, err := h.ArticleService.CreateArticle(requestArticleDTO)
if err != nil {
return utils.GenericErrorResponse(c, fiber.StatusBadRequest, err.Error())
}
return utils.CreateResponse(c, articleResponse, "Article created successfully")
}

View File

@ -0,0 +1,61 @@
package repositories
import (
"github.com/pahmiudahgede/senggoldong/model"
"gorm.io/gorm"
)
type ArticleRepository interface {
CreateArticle(article *model.Article) error
FindArticleByID(id string) (*model.Article, error)
FindAllArticles(page, limit int) ([]model.Article, int, error)
}
type articleRepository struct {
DB *gorm.DB
}
func NewArticleRepository(db *gorm.DB) ArticleRepository {
return &articleRepository{DB: db}
}
func (r *articleRepository) CreateArticle(article *model.Article) error {
err := r.DB.Create(article).Error
if err != nil {
return err
}
return nil
}
func (r *articleRepository) FindArticleByID(id string) (*model.Article, error) {
var article model.Article
err := r.DB.Where("id = ?", id).First(&article).Error
if err != nil {
return nil, err
}
return &article, nil
}
func (r *articleRepository) FindAllArticles(page, limit int) ([]model.Article, int, error) {
var articles []model.Article
var total int64
err := r.DB.Model(&model.Article{}).Count(&total).Error
if err != nil {
return nil, 0, err
}
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&articles).Error
if err != nil {
return nil, 0, err
}
} else {
err := r.DB.Find(&articles).Error
if err != nil {
return nil, 0, err
}
}
return articles, int(total), nil
}

View File

@ -0,0 +1,64 @@
package services
import (
"fmt"
"time"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/model"
"github.com/pahmiudahgede/senggoldong/utils"
)
type ArticleService interface {
CreateArticle(articleDTO dto.RequestArticleDTO) (*dto.ArticleResponseDTO, error)
}
type articleService struct {
ArticleRepo repositories.ArticleRepository
}
func NewArticleService(articleRepo repositories.ArticleRepository) ArticleService {
return &articleService{ArticleRepo: articleRepo}
}
func (s *articleService) CreateArticle(articleDTO dto.RequestArticleDTO) (*dto.ArticleResponseDTO, error) {
article := &model.Article{
Title: articleDTO.Title,
CoverImage: articleDTO.CoverImage,
Author: articleDTO.Author,
Heading: articleDTO.Heading,
Content: articleDTO.Content,
}
err := s.ArticleRepo.CreateArticle(article)
if err != nil {
return nil, fmt.Errorf("failed to create article: %v", err)
}
publishedAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
articleResponseDTO := &dto.ArticleResponseDTO{
ID: article.ID,
Title: article.Title,
CoverImage: article.CoverImage,
Author: article.Author,
Heading: article.Heading,
Content: article.Content,
PublishedAt: publishedAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("article:%s", article.ID)
cacheData := map[string]interface{}{
"data": articleResponseDTO,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching article to Redis: %v\n", err)
}
return articleResponseDTO, nil
}

14
model/article_model.go Normal file
View File

@ -0,0 +1,14 @@
package model
import "time"
type Article struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
Title string `gorm:"not null" json:"title"`
CoverImage string `gorm:"not null" json:"coverImage"`
Author string `gorm:"not null" json:"author"`
Heading string `gorm:"not null" json:"heading"`
Content string `gorm:"not null" json:"content"`
PublishedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

@ -0,0 +1,21 @@
package presentation
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/internal/handler"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/internal/services"
"github.com/pahmiudahgede/senggoldong/middleware"
"github.com/pahmiudahgede/senggoldong/utils"
)
func ArticleRouter(api fiber.Router) {
articleRepo := repositories.NewArticleRepository(config.DB)
articleService := services.NewArticleService(articleRepo)
articleHandler := handler.NewArticleHandler(articleService)
articleAPI := api.Group("/article-rijik")
articleAPI.Post("/create-article", middleware.AuthMiddleware, middleware.RoleMiddleware(utils.RoleAdministrator), articleHandler.CreateArticle)
}

View File

@ -16,4 +16,5 @@ func SetupRoutes(app *fiber.App) {
presentation.RoleRouter(api) presentation.RoleRouter(api)
presentation.WilayahRouter(api) presentation.WilayahRouter(api)
presentation.AddressRouter(api) presentation.AddressRouter(api)
presentation.ArticleRouter(api)
} }