feat: update articles & articles_categories

This commit is contained in:
hanif salafi 2025-01-20 11:43:40 +07:00
parent ea0637aac2
commit 58c3bfc283
14 changed files with 141 additions and 42 deletions

View File

@ -8,6 +8,7 @@ type ArticleCategories struct {
Description string `json:"description" gorm:"type:varchar"`
ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"`
ParentId *int `json:"parent_id" gorm:"type:int4"`
Tags *string `json:"tags" gorm:"type:varchar"`
Position *int `json:"position" gorm:"type:int4"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4;default:1"`

View File

@ -3,10 +3,10 @@ package entity
import "time"
type ArticleCategoryDetails struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ArticleId int `json:"article_id" gorm:"type:int4"`
CategoryId int `json:"category_id" gorm:"type:int4"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ArticleId uint `json:"article_id" gorm:"type:int4"`
CategoryId int `json:"category_id" gorm:"type:int4"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -4,16 +4,16 @@ import (
"go-humas-be/app/database/entity"
res "go-humas-be/app/module/article_categories/response"
"strconv"
"strings"
)
func ArticleCategoriesResponseMapper(articleCategoriesReq *entity.ArticleCategories) (articleCategoriesRes *res.ArticleCategoriesResponse) {
func ArticleCategoriesResponseMapper(articleCategoriesReq *entity.ArticleCategories, host string) (articleCategoriesRes *res.ArticleCategoriesResponse) {
if articleCategoriesReq != nil {
articleCategoriesRes = &res.ArticleCategoriesResponse{
ID: articleCategoriesReq.ID,
Title: articleCategoriesReq.Title,
Description: articleCategoriesReq.Description,
ThumbnailPath: articleCategoriesReq.ThumbnailPath,
ThumbnailUrl: "/article-categories/thumbnail/viewer/" + strconv.Itoa(int(articleCategoriesReq.ID)),
ParentId: articleCategoriesReq.ParentId,
CreatedById: articleCategoriesReq.CreatedById,
StatusId: articleCategoriesReq.StatusId,
@ -23,6 +23,15 @@ func ArticleCategoriesResponseMapper(articleCategoriesReq *entity.ArticleCategor
CreatedAt: articleCategoriesReq.CreatedAt,
UpdatedAt: articleCategoriesReq.UpdatedAt,
}
if articleCategoriesReq.Tags != nil {
tagsValue := *articleCategoriesReq.Tags
articleCategoriesRes.Tags = strings.Split(tagsValue, ",")
}
if articleCategoriesRes.ThumbnailPath != nil {
articleCategoriesRes.ThumbnailUrl = host + "/article-categories/thumbnail/viewer/" + strconv.Itoa(int(articleCategoriesReq.ID))
}
}
return articleCategoriesRes
}

View File

@ -21,16 +21,18 @@ type ArticleCategoriesQueryRequest struct {
}
type ArticleCategoriesCreateRequest struct {
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
StatusId int `json:"statusId" validate:"required"`
ParentId *int `json:"parentId"`
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
StatusId int `json:"statusId" validate:"required"`
Tags *string `json:"tags"`
ParentId *int `json:"parentId"`
}
func (req ArticleCategoriesCreateRequest) ToEntity() *entity.ArticleCategories {
return &entity.ArticleCategories{
Title: req.Title,
Description: req.Description,
Tags: req.Tags,
ParentId: req.ParentId,
StatusId: req.StatusId,
}
@ -41,6 +43,7 @@ type ArticleCategoriesUpdateRequest struct {
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
StatusId int `json:"statusId" validate:"required"`
Tags *string `json:"tags"`
ParentId *int `json:"parentId"`
IsPublish *bool `json:"isPublish"`
PublishedAt *time.Time `json:"publishedAt"`
@ -52,6 +55,7 @@ func (req ArticleCategoriesUpdateRequest) ToEntity() *entity.ArticleCategories {
Title: req.Title,
Description: req.Description,
ParentId: req.ParentId,
Tags: req.Tags,
StatusId: req.StatusId,
IsPublish: req.IsPublish,
PublishedAt: req.PublishedAt,

View File

@ -6,8 +6,9 @@ type ArticleCategoriesResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
ThumbnailPath *string `json:"thumbnailPath"`
ThumbnailUrl string `json:"thumbnailUrl"`
Tags []string `json:"tags"`
ThumbnailPath *string `json:"thumbnailPath"`
ParentId *int `json:"parentId"`
CreatedById *uint `json:"createdById"`
StatusId int `json:"statusId"`

View File

@ -11,6 +11,7 @@ import (
"go-humas-be/app/module/article_categories/request"
"go-humas-be/app/module/article_categories/response"
usersRepository "go-humas-be/app/module/users/repository"
config "go-humas-be/config/config"
minioStorage "go-humas-be/config/config"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
@ -30,6 +31,7 @@ type articleCategoriesService struct {
UsersRepo usersRepository.UsersRepository
MinioStorage *minioStorage.MinioStorage
Log zerolog.Logger
Cfg *config.Config
}
// ArticleCategoriesService define interface of IArticleCategoriesService
@ -44,13 +46,14 @@ type ArticleCategoriesService interface {
}
// NewArticleCategoriesService init ArticleCategoriesService
func NewArticleCategoriesService(repo repository.ArticleCategoriesRepository, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, log zerolog.Logger) ArticleCategoriesService {
func NewArticleCategoriesService(repo repository.ArticleCategoriesRepository, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, log zerolog.Logger, cfg *config.Config) ArticleCategoriesService {
return &articleCategoriesService{
Repo: repo,
UsersRepo: usersRepo,
MinioStorage: minioStorage,
Log: log,
Cfg: cfg,
}
}
@ -61,8 +64,10 @@ func (_i *articleCategoriesService) All(req request.ArticleCategoriesQueryReques
return
}
host := _i.Cfg.App.Domain
port := _i.Cfg.App.ExternalPort
for _, result := range results {
articleCategoriess = append(articleCategoriess, mapper.ArticleCategoriesResponseMapper(result))
articleCategoriess = append(articleCategoriess, mapper.ArticleCategoriesResponseMapper(result, host+port))
}
return
@ -73,8 +78,9 @@ func (_i *articleCategoriesService) Show(id uint) (articleCategories *response.A
if err != nil {
return nil, err
}
return mapper.ArticleCategoriesResponseMapper(result), nil
host := _i.Cfg.App.Domain
port := _i.Cfg.App.ExternalPort
return mapper.ArticleCategoriesResponseMapper(result, host+port), nil
}
func (_i *articleCategoriesService) Save(req request.ArticleCategoriesCreateRequest, authToken string) (articleCategories *entity.ArticleCategories, err error) {

View File

@ -18,7 +18,7 @@ type ArticleCategoryDetailsQueryRequest struct {
}
type ArticleCategoryDetailsCreateRequest struct {
ArticleId int `json:"article_id" validate:"required"`
ArticleId uint `json:"article_id" validate:"required"`
CategoryId int `json:"category_id" validate:"required"`
IsActive bool `json:"is_active" validate:"required"`
}
@ -33,7 +33,7 @@ func (req ArticleCategoryDetailsCreateRequest) ToEntity() *entity.ArticleCategor
type ArticleCategoryDetailsUpdateRequest struct {
ID uint `json:"id" validate:"required"`
ArticleId int `json:"article_id" validate:"required"`
ArticleId uint `json:"article_id" validate:"required"`
CategoryId int `json:"category_id" validate:"required"`
IsActive bool `json:"is_active" validate:"required"`
CreatedAt time.Time `json:"created_at"`

View File

@ -13,15 +13,12 @@ import (
func ArticlesResponseMapper(
log zerolog.Logger,
host string,
articlesReq *entity.Articles,
articleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository,
articleFilesRepo articleFilesRepository.ArticleFilesRepository,
usersRepo usersRepository.UsersRepository,
) (articlesRes *res.ArticlesResponse) {
thumbnailUrl := "/articles/thumbnail/viewer/"
if articlesReq.ThumbnailName != nil {
thumbnailUrl += *articlesReq.ThumbnailName
}
createdByName := ""
if articlesReq.CreatedById != nil {
@ -56,7 +53,6 @@ func ArticlesResponseMapper(
HtmlDescription: articlesReq.HtmlDescription,
TypeId: articlesReq.TypeId,
Tags: articlesReq.Tags,
ThumbnailUrl: thumbnailUrl,
CategoryId: articlesReq.CategoryId,
CategoryName: categoryName,
PageUrl: articlesReq.PageUrl,
@ -73,6 +69,10 @@ func ArticlesResponseMapper(
UpdatedAt: articlesReq.UpdatedAt,
ArticleFiles: articleFilesArr,
}
if articlesReq.ThumbnailName != nil {
articlesRes.ThumbnailUrl = host + "/articles/thumbnail/viewer/" + *articlesReq.ThumbnailName
}
}
return articlesRes

View File

@ -29,6 +29,7 @@ type ArticlesCreateRequest struct {
Description string `json:"description" validate:"required"`
HtmlDescription string `json:"htmlDescription" validate:"required"`
CategoryId int `json:"categoryId" validate:"required"`
CategoryIds string `json:"categoryIds" validate:"required"`
TypeId int `json:"typeId" validate:"required"`
Tags string `json:"tags" validate:"required"`
OldId *uint `json:"oldId"`

View File

@ -2,17 +2,21 @@ package service
import (
"context"
"errors"
"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
"github.com/rs/zerolog"
"go-humas-be/app/database/entity"
articleCategoriesRepository "go-humas-be/app/module/article_categories/repository"
articleCategoryDetailsRepository "go-humas-be/app/module/article_category_details/repository"
articleCategoryDetailsReq "go-humas-be/app/module/article_category_details/request"
articleFilesRepository "go-humas-be/app/module/article_files/repository"
"go-humas-be/app/module/articles/mapper"
"go-humas-be/app/module/articles/repository"
"go-humas-be/app/module/articles/request"
"go-humas-be/app/module/articles/response"
usersRepository "go-humas-be/app/module/users/repository"
config "go-humas-be/config/config"
minioStorage "go-humas-be/config/config"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
@ -28,12 +32,14 @@ import (
// ArticlesService
type articlesService struct {
Repo repository.ArticlesRepository
ArticleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository
ArticleFilesRepo articleFilesRepository.ArticleFilesRepository
Log zerolog.Logger
UsersRepo usersRepository.UsersRepository
MinioStorage *minioStorage.MinioStorage
Repo repository.ArticlesRepository
ArticleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository
ArticleFilesRepo articleFilesRepository.ArticleFilesRepository
ArticleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository
Log zerolog.Logger
Cfg *config.Config
UsersRepo usersRepository.UsersRepository
MinioStorage *minioStorage.MinioStorage
}
// ArticlesService define interface of IArticlesService
@ -51,19 +57,23 @@ type ArticlesService interface {
func NewArticlesService(
repo repository.ArticlesRepository,
articleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository,
articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository,
articleFilesRepo articleFilesRepository.ArticleFilesRepository,
log zerolog.Logger,
cfg *config.Config,
usersRepo usersRepository.UsersRepository,
minioStorage *minioStorage.MinioStorage,
) ArticlesService {
return &articlesService{
Repo: repo,
ArticleCategoriesRepo: articleCategoriesRepo,
ArticleFilesRepo: articleFilesRepo,
Log: log,
UsersRepo: usersRepo,
MinioStorage: minioStorage,
Repo: repo,
ArticleCategoriesRepo: articleCategoriesRepo,
ArticleCategoryDetailsRepo: articleCategoryDetailsRepo,
ArticleFilesRepo: articleFilesRepo,
Log: log,
UsersRepo: usersRepo,
MinioStorage: minioStorage,
Cfg: cfg,
}
}
@ -78,8 +88,11 @@ func (_i *articlesService) All(req request.ArticlesQueryRequest) (articless []*r
Format(time.RFC3339)).Str("Service:articlesService", "Methods:All").
Interface("results", results).Msg("")
host := _i.Cfg.App.Domain
port := _i.Cfg.App.ExternalPort
for _, result := range results {
articleRes := mapper.ArticlesResponseMapper(_i.Log, result, _i.ArticleCategoriesRepo, _i.ArticleFilesRepo, _i.UsersRepo)
articleRes := mapper.ArticlesResponseMapper(_i.Log, host+port, result, _i.ArticleCategoriesRepo, _i.ArticleFilesRepo, _i.UsersRepo)
articless = append(articless, articleRes)
}
@ -92,7 +105,10 @@ func (_i *articlesService) Show(id uint) (articles *response.ArticlesResponse, e
return nil, err
}
return mapper.ArticlesResponseMapper(_i.Log, result, _i.ArticleCategoriesRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
host := _i.Cfg.App.Domain
port := _i.Cfg.App.ExternalPort
return mapper.ArticlesResponseMapper(_i.Log, host+port, result, _i.ArticleCategoriesRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
}
func (_i *articlesService) Save(req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) {
@ -102,7 +118,41 @@ func (_i *articlesService) Save(req request.ArticlesCreateRequest, authToken str
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
newReq.CreatedById = &createdBy.ID
return _i.Repo.Create(newReq)
saveArticleRepo, err := _i.Repo.Create(newReq)
if err != nil {
return nil, err
}
var categoryIds []string
if req.CategoryIds != "" {
categoryIds = strings.Split(req.CategoryIds, ",")
}
for categoryId := range categoryIds {
findCategory, err := _i.ArticleCategoriesRepo.FindOne(uint(categoryId))
if err != nil {
return nil, err
}
if findCategory != nil {
return nil, errors.New("category not found")
}
categoryReq := articleCategoryDetailsReq.ArticleCategoryDetailsCreateRequest{
ArticleId: saveArticleRepo.ID,
CategoryId: categoryId,
IsActive: true,
}
newCategoryReq := categoryReq.ToEntity()
err = _i.ArticleCategoryDetailsRepo.Create(newCategoryReq)
if err != nil {
return nil, err
}
}
return saveArticleRepo, nil
}
func (_i *articlesService) SaveThumbnail(c *fiber.Ctx) (err error) {

View File

@ -3,7 +3,7 @@
name = "Fiber starter"
host = "http://38.47.180.165"
port = ":8800"
domain = "https://38.47.180.165"
domain = "http://38.47.180.165"
external-port = ":8802"
idle-timeout = 5 # As seconds
print-routes = false
@ -13,7 +13,7 @@ body-limit = 1048576000 # "100 * 1024 * 1024"
[db.postgres]
dsn = "postgresql://humas_user:HumasDB@2024@38.47.180.165:5432/humas_db" # <driver>://<username>:<password>@<host>:<port>/<database>
migrate = false
migrate = true
seed = false
[logger]

View File

@ -6125,6 +6125,9 @@ const docTemplate = `{
"statusId": {
"type": "integer"
},
"tags": {
"type": "string"
},
"title": {
"type": "string"
}
@ -6157,6 +6160,9 @@ const docTemplate = `{
"statusId": {
"type": "integer"
},
"tags": {
"type": "string"
},
"title": {
"type": "string"
}
@ -6302,6 +6308,7 @@ const docTemplate = `{
"type": "object",
"required": [
"categoryId",
"categoryIds",
"description",
"htmlDescription",
"slug",
@ -6313,6 +6320,9 @@ const docTemplate = `{
"categoryId": {
"type": "integer"
},
"categoryIds": {
"type": "string"
},
"description": {
"type": "string"
},

View File

@ -6114,6 +6114,9 @@
"statusId": {
"type": "integer"
},
"tags": {
"type": "string"
},
"title": {
"type": "string"
}
@ -6146,6 +6149,9 @@
"statusId": {
"type": "integer"
},
"tags": {
"type": "string"
},
"title": {
"type": "string"
}
@ -6291,6 +6297,7 @@
"type": "object",
"required": [
"categoryId",
"categoryIds",
"description",
"htmlDescription",
"slug",
@ -6302,6 +6309,9 @@
"categoryId": {
"type": "integer"
},
"categoryIds": {
"type": "string"
},
"description": {
"type": "string"
},

View File

@ -26,6 +26,8 @@ definitions:
type: integer
statusId:
type: integer
tags:
type: string
title:
type: string
required:
@ -47,6 +49,8 @@ definitions:
type: string
statusId:
type: integer
tags:
type: string
title:
type: string
required:
@ -155,6 +159,8 @@ definitions:
properties:
categoryId:
type: integer
categoryIds:
type: string
description:
type: string
htmlDescription:
@ -171,6 +177,7 @@ definitions:
type: integer
required:
- categoryId
- categoryIds
- description
- htmlDescription
- slug