diff --git a/app/database/entity/article_categories.entity.go b/app/database/entity/article_categories.entity.go index e7b3bdd..63d733d 100644 --- a/app/database/entity/article_categories.entity.go +++ b/app/database/entity/article_categories.entity.go @@ -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"` diff --git a/app/database/entity/article_category_details.entity.go b/app/database/entity/article_category_details.entity.go index e7a1f5f..427f550 100644 --- a/app/database/entity/article_category_details.entity.go +++ b/app/database/entity/article_category_details.entity.go @@ -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()"` -} \ No newline at end of file + 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()"` +} diff --git a/app/module/article_categories/mapper/article_categories.mapper.go b/app/module/article_categories/mapper/article_categories.mapper.go index 205c0c2..1ef5a51 100644 --- a/app/module/article_categories/mapper/article_categories.mapper.go +++ b/app/module/article_categories/mapper/article_categories.mapper.go @@ -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 } diff --git a/app/module/article_categories/request/article_categories.request.go b/app/module/article_categories/request/article_categories.request.go index 3a90951..ac00825 100644 --- a/app/module/article_categories/request/article_categories.request.go +++ b/app/module/article_categories/request/article_categories.request.go @@ -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, diff --git a/app/module/article_categories/response/article_categories.response.go b/app/module/article_categories/response/article_categories.response.go index 9327cf8..f25946c 100644 --- a/app/module/article_categories/response/article_categories.response.go +++ b/app/module/article_categories/response/article_categories.response.go @@ -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"` diff --git a/app/module/article_categories/service/article_categories.service.go b/app/module/article_categories/service/article_categories.service.go index bd3c4cc..1b39195 100644 --- a/app/module/article_categories/service/article_categories.service.go +++ b/app/module/article_categories/service/article_categories.service.go @@ -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) { diff --git a/app/module/article_category_details/request/article_category_details.request.go b/app/module/article_category_details/request/article_category_details.request.go index 8c8f749..de215cf 100644 --- a/app/module/article_category_details/request/article_category_details.request.go +++ b/app/module/article_category_details/request/article_category_details.request.go @@ -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"` diff --git a/app/module/articles/mapper/articles.mapper.go b/app/module/articles/mapper/articles.mapper.go index db6df39..dbef55e 100644 --- a/app/module/articles/mapper/articles.mapper.go +++ b/app/module/articles/mapper/articles.mapper.go @@ -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 diff --git a/app/module/articles/request/articles.request.go b/app/module/articles/request/articles.request.go index 60b2fa5..0994d0e 100644 --- a/app/module/articles/request/articles.request.go +++ b/app/module/articles/request/articles.request.go @@ -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"` diff --git a/app/module/articles/service/articles.service.go b/app/module/articles/service/articles.service.go index 549b3f3..d4ed631 100644 --- a/app/module/articles/service/articles.service.go +++ b/app/module/articles/service/articles.service.go @@ -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) { diff --git a/config/toml/config.toml b/config/toml/config.toml index 4ce8e31..44ac923 100644 --- a/config/toml/config.toml +++ b/config/toml/config.toml @@ -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" # ://:@:/ -migrate = false +migrate = true seed = false [logger] diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index d605b72..e0547ac 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -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" }, diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 15bbbb5..582e37a 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -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" }, diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index ec74181..267bc39 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -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