feat: add feature create article
This commit is contained in:
parent
26cbe82891
commit
a9f2aec4ec
|
@ -43,6 +43,7 @@ func ConnectDatabase() {
|
|||
&model.Role{},
|
||||
&model.UserPin{},
|
||||
&model.Address{},
|
||||
&model.Article{},
|
||||
// ==main feature==
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -16,4 +16,5 @@ func SetupRoutes(app *fiber.App) {
|
|||
presentation.RoleRouter(api)
|
||||
presentation.WilayahRouter(api)
|
||||
presentation.AddressRouter(api)
|
||||
presentation.ArticleRouter(api)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue