feat: add feature create article
This commit is contained in:
parent
26cbe82891
commit
a9f2aec4ec
|
@ -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 {
|
||||||
|
|
|
@ -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.RoleRouter(api)
|
||||||
presentation.WilayahRouter(api)
|
presentation.WilayahRouter(api)
|
||||||
presentation.AddressRouter(api)
|
presentation.AddressRouter(api)
|
||||||
|
presentation.ArticleRouter(api)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue