diff --git a/app/database/entity/articles.entity.go b/app/database/entity/articles.entity.go index ddd62ee..19b1968 100644 --- a/app/database/entity/articles.entity.go +++ b/app/database/entity/articles.entity.go @@ -16,9 +16,9 @@ type Articles struct { PageUrl *string `json:"page_url" gorm:"type:varchar"` CreatedById *uint `json:"created_by_id" gorm:"type:int4"` AiArticleId *int `json:"ai_article_id" gorm:"type:int4"` - CommentCount *int `json:"comment_count" gorm:"type:int4"` - ShareCount *int `json:"share_count" gorm:"type:int4"` - ViewCount *int `json:"view_count" gorm:"type:int4"` + CommentCount *int `json:"comment_count" gorm:"type:int4;default:0"` + ShareCount *int `json:"share_count" gorm:"type:int4;default:0"` + ViewCount *int `json:"view_count" gorm:"type:int4;default:0"` StatusId *int `json:"status_id" gorm:"type:int4"` OldId *uint `json:"old_id" gorm:"type:int4"` IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` diff --git a/app/database/entity/users.entity.go b/app/database/entity/users.entity.go deleted file mode 100644 index 175b097..0000000 --- a/app/database/entity/users.entity.go +++ /dev/null @@ -1,30 +0,0 @@ -package entity - -import "time" - -type Users struct { - ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` - Username string `json:"username" gorm:"type:varchar"` - Email string `json:"email" gorm:"type:varchar"` - Fullname string `json:"fullname" gorm:"type:varchar"` - Address *string `json:"address" gorm:"type:varchar"` - PhoneNumber *string `json:"phone_number" gorm:"type:varchar"` - WorkType *string `json:"work_type" gorm:"type:varchar"` - GenderType *string `json:"gender_type" gorm:"type:varchar"` - IdentityType *string `json:"identity_type" gorm:"type:varchar"` - IdentityGroup *string `json:"identity_group" gorm:"type:varchar"` - IdentityGroupNumber *string `json:"identity_group_number" gorm:"type:varchar"` - IdentityNumber *string `json:"identity_number" gorm:"type:varchar"` - DateOfBirth *string `json:"date_of_birth" gorm:"type:varchar"` - LastEducation *string `json:"last_education" gorm:"type:varchar"` - UserRoleId uint `json:"user_role_id" gorm:"type:int4"` - UserLevelId uint `json:"user_level_id" gorm:"type:int4"` - KeycloakId *string `json:"keycloak_id" gorm:"type:varchar"` - StatusId *int `json:"status_id" gorm:"type:int4;default:1"` - CreatedById *uint `json:"created_by_id" gorm:"type:int4"` - ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"` - TempPassword *string `json:"temp_password" gorm:"type:varchar"` - IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` - CreatedAt time.Time `json:"created_at" gorm:"default:now()"` - UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` -} diff --git a/app/database/entity/users/users.entity.go b/app/database/entity/users/users.entity.go new file mode 100644 index 0000000..ffe6b62 --- /dev/null +++ b/app/database/entity/users/users.entity.go @@ -0,0 +1,34 @@ +package users + +import ( + "go-humas-be/app/database/entity" + "time" +) + +type Users struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Username string `json:"username" gorm:"type:varchar"` + Email string `json:"email" gorm:"type:varchar"` + Fullname string `json:"fullname" gorm:"type:varchar"` + Address *string `json:"address" gorm:"type:varchar"` + PhoneNumber *string `json:"phone_number" gorm:"type:varchar"` + WorkType *string `json:"work_type" gorm:"type:varchar"` + GenderType *string `json:"gender_type" gorm:"type:varchar"` + IdentityType *string `json:"identity_type" gorm:"type:varchar"` + IdentityGroup *string `json:"identity_group" gorm:"type:varchar"` + IdentityGroupNumber *string `json:"identity_group_number" gorm:"type:varchar"` + IdentityNumber *string `json:"identity_number" gorm:"type:varchar"` + DateOfBirth *string `json:"date_of_birth" gorm:"type:varchar"` + LastEducation *string `json:"last_education" gorm:"type:varchar"` + UserRoleId uint `json:"user_role_id" gorm:"type:int4"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4"` + UserLevel *entity.UserLevels `json:"user_levels" gorm:"foreignKey:UserLevelId;references:ID"` + KeycloakId *string `json:"keycloak_id" gorm:"type:varchar"` + StatusId *int `json:"status_id" gorm:"type:int4;default:1"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"` + TempPassword *string `json:"temp_password" gorm:"type:varchar"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/index.database.go b/app/database/index.database.go index 6b6b27d..c6d6214 100644 --- a/app/database/index.database.go +++ b/app/database/index.database.go @@ -4,9 +4,11 @@ import ( "github.com/rs/zerolog" "go-humas-be/app/database/entity" "go-humas-be/app/database/entity/article_category_details" + "go-humas-be/app/database/entity/users" "go-humas-be/config/config" "gorm.io/driver/postgres" "gorm.io/gorm" + "gorm.io/gorm/logger" ) // Database setup database with gorm @@ -32,7 +34,20 @@ func NewDatabase(cfg *config.Config, log zerolog.Logger) *Database { // ConnectDatabase connect database func (_db *Database) ConnectDatabase() { - conn, err := gorm.Open(postgres.Open(_db.Cfg.DB.Postgres.DSN), &gorm.Config{}) + logMode := _db.Cfg.DB.Postgres.LogMode + var logLevel logger.LogLevel + if logMode == "INFO" { + logLevel = logger.Info + } else if logMode == "WARN" { + logLevel = logger.Warn + } else if logMode == "ERROR" { + logLevel = logger.Error + } else if logMode == "NONE" { + logLevel = logger.Silent + } + conn, err := gorm.Open(postgres.Open(_db.Cfg.DB.Postgres.DSN), &gorm.Config{ + Logger: logger.Default.LogMode(logLevel), + }) if err != nil { _db.Log.Error().Err(err).Msg("An unknown error occurred when to connect the database!") } else { @@ -91,7 +106,7 @@ func Models() []interface{} { entity.UserLevels{}, entity.UserRoles{}, entity.UserRoleAccesses{}, - entity.Users{}, + users.Users{}, entity.UserRoleLevelDetails{}, } } diff --git a/app/module/articles/articles.module.go b/app/module/articles/articles.module.go index cf26b02..5c69729 100644 --- a/app/module/articles/articles.module.go +++ b/app/module/articles/articles.module.go @@ -52,5 +52,7 @@ func (_i *ArticlesRouter) RegisterArticlesRoutes() { router.Get("/thumbnail/viewer/:thumbnailName", articlesController.Viewer) router.Delete("/:id", articlesController.Delete) router.Get("/statistic/summary", articlesController.SummaryStats) + router.Get("/statistic/user-levels", articlesController.ArticlePerUserLevelStats) + router.Get("/statistic/monthly", articlesController.ArticleMonthlyStats) }) } diff --git a/app/module/articles/controller/articles.controller.go b/app/module/articles/controller/articles.controller.go index 3a75da1..e6ce1db 100644 --- a/app/module/articles/controller/articles.controller.go +++ b/app/module/articles/controller/articles.controller.go @@ -24,6 +24,8 @@ type ArticlesController interface { Delete(c *fiber.Ctx) error Viewer(c *fiber.Ctx) error SummaryStats(c *fiber.Ctx) error + ArticlePerUserLevelStats(c *fiber.Ctx) error + ArticleMonthlyStats(c *fiber.Ctx) error } func NewArticlesController(articlesService service.ArticlesService) ArticlesController { @@ -264,3 +266,65 @@ func (_i *articlesController) SummaryStats(c *fiber.Ctx) error { Data: response, }) } + +// ArticlePerUserLevelStats Articles +// @Summary ArticlePerUserLevelStats Articles +// @Description API for ArticlePerUserLevelStats of Article +// @Tags Articles +// @Security Bearer +// @Param Authorization header string true "Insert your access token" default(Bearer ) +// @Param startDate query string false "start date" +// @Param endDate query string false "start date" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/statistic/user-levels [get] +func (_i *articlesController) ArticlePerUserLevelStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + startDate := c.Query("startDate") + endDate := c.Query("endDate") + + response, err := _i.articlesService.ArticlePerUserLevelStats(authToken, &startDate, &endDate) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticlePerUserLevelStats of Articles successfully retrieved"}, + Data: response, + }) +} + +// ArticleMonthlyStats Articles +// @Summary ArticleMonthlyStats Articles +// @Description API for ArticleMonthlyStats of Article +// @Tags Articles +// @Security Bearer +// @Param Authorization header string true "Insert your access token" default(Bearer ) +// @Param year query int false "year" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/statistic/monthly [get] +func (_i *articlesController) ArticleMonthlyStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + year := c.Query("year") + yearInt, err := strconv.Atoi(year) + if err != nil { + return err + } + + response, err := _i.articlesService.ArticleMonthlyStats(authToken, &yearInt) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleMonthlyStats of Articles successfully retrieved"}, + Data: response, + }) +} diff --git a/app/module/articles/repository/articles.repository.go b/app/module/articles/repository/articles.repository.go index 7f5af64..2e4d58e 100644 --- a/app/module/articles/repository/articles.repository.go +++ b/app/module/articles/repository/articles.repository.go @@ -26,6 +26,8 @@ type ArticlesRepository interface { Update(id uint, articles *entity.Articles) (err error) Delete(id uint) (err error) SummaryStats(userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) + ArticlePerUserLevelStats(userLevelId *uint, levelNumber *int, startDate *time.Time, endDate *time.Time) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) + ArticleMonthlyStats(userLevelId *uint, levelNumber *int, year int) (articleMontlyStats []*response.ArticleMonthlyStats, err error) } func NewArticlesRepository(db *database.Database, log zerolog.Logger) ArticlesRepository { @@ -153,3 +155,134 @@ func (_i *articlesRepository) SummaryStats(userID uint) (articleSummaryStats *re return articleSummaryStats, err } + +func (_i *articlesRepository) ArticlePerUserLevelStats(userLevelId *uint, levelNumber *int, startDate *time.Time, endDate *time.Time) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) { + + levelNumberTop := 1 + + query := _i.DB.DB.Model(&entity.Articles{}). + Select("user_levels.id as user_level_id", "user_levels.name as user_level_name", "COUNT(articles.id) as total_article"). + Joins("LEFT JOIN users ON articles.created_by_id = users.id"). + Joins("LEFT JOIN user_levels ON users.user_level_id = user_levels.id"). + Where("articles.is_active = true") + + if userLevelId != nil && *levelNumber != levelNumberTop { + query = query.Where("user_levels.id = ? or user_levels.parent_level_id = ?", *userLevelId, *userLevelId) + } else { + query = _i.DB.DB.Raw(` + WITH LevelHierarchy AS ( + SELECT + id, + name, + level_number, + parent_level_id, + CASE + WHEN level_number = 2 THEN id + WHEN level_number = 3 THEN parent_level_id + END AS level_2_id, + CASE + WHEN level_number = 2 THEN name + WHEN level_number = 3 THEN (SELECT name FROM user_levels ul2 WHERE ul2.id = user_levels.parent_level_id) + END AS level_2_name + FROM user_levels + ) + SELECT + lh.level_2_id AS user_level_id, + lh.level_2_name AS user_level_name, + COUNT(articles.id) AS total_article + FROM articles + JOIN users ON articles.created_by_id = users.id + JOIN LevelHierarchy lh ON users.user_level_id = lh.id + WHERE articles.is_active = true AND lh.level_2_id > 0 + GROUP BY + lh.level_2_id, + lh.level_2_name + ORDER BY + total_article DESC`) + } + + // Apply date filters if provided + if startDate != nil { + query = query.Where("articles.created_at >= ?", *startDate) + } + if endDate != nil { + query = query.Where("articles.created_at <= ?", *endDate) + } + + // Group by all non-aggregated columns + err = query.Group("user_levels.id, user_levels.name"). + Order("total_article DESC"). + Scan(&articlePerUserLevelStats).Error + + return articlePerUserLevelStats, err +} + +func (_i *articlesRepository) ArticleMonthlyStats(userLevelId *uint, levelNumber *int, year int) (articleMontlyStats []*response.ArticleMonthlyStats, err error) { + levelNumberTop := 1 + + if year < 1900 || year > 2100 { + return nil, fmt.Errorf("invalid year") + } + + var results []struct { + Month int + Day int + TotalView int + TotalComment int + TotalShare int + } + + query := _i.DB.DB.Model(&entity.Articles{}). + Select("EXTRACT(MONTH FROM created_at) as month, EXTRACT(DAY FROM created_at) as day, "+ + "SUM(view_count) as total_view, "+ + "SUM(comment_count) as total_comment, "+ + "SUM(share_count) as total_share"). + Where("EXTRACT(YEAR FROM created_at) = ?", year) + + if userLevelId != nil && *levelNumber != levelNumberTop { + query = _i.DB.DB.Model(&entity.Articles{}). + Select("EXTRACT(MONTH FROM articles.created_at) as month, EXTRACT(DAY FROM articles.created_at) as day, "+ + "SUM(articles.view_count) as total_view, "+ + "SUM(articles.comment_count) as total_comment, "+ + "SUM(articles.share_count) as total_share"). + Joins("LEFT JOIN users ON articles.created_by_id = users.id"). + Joins("LEFT JOIN user_levels ON users.user_level_id = user_levels.id"). + Where("articles.is_active = true"). + Where("EXTRACT(YEAR FROM articles.created_at) = ?", year). + Where("(user_levels.id = ? OR user_levels.parent_level_id = ?)", *userLevelId, *userLevelId) + } + + err = query.Group("month, day").Scan(&results).Error + if err != nil { + return nil, err + } + + // Siapkan struktur untuk menyimpan data bulanan + monthlyAnalytics := make([]*response.ArticleMonthlyStats, 12) + for i := 0; i < 12; i++ { + daysInMonth := time.Date(year, time.Month(i+1), 0, 0, 0, 0, 0, time.UTC).Day() + monthlyAnalytics[i] = &response.ArticleMonthlyStats{ + Year: year, + Month: i + 1, + View: make([]int, daysInMonth), + Comment: make([]int, daysInMonth), + Share: make([]int, daysInMonth), + } + } + + // Isi data dari hasil agregasi + for _, result := range results { + monthIndex := result.Month - 1 + dayIndex := result.Day - 1 + + if monthIndex >= 0 && monthIndex < 12 { + if dayIndex >= 0 && dayIndex < len(monthlyAnalytics[monthIndex].View) { + monthlyAnalytics[monthIndex].View[dayIndex] = result.TotalView + monthlyAnalytics[monthIndex].Comment[dayIndex] = result.TotalComment + monthlyAnalytics[monthIndex].Share[dayIndex] = result.TotalShare + } + } + } + + return monthlyAnalytics, nil +} diff --git a/app/module/articles/response/articles.response.go b/app/module/articles/response/articles.response.go index e3f5a52..dc248f3 100644 --- a/app/module/articles/response/articles.response.go +++ b/app/module/articles/response/articles.response.go @@ -43,3 +43,17 @@ type ArticleSummaryStats struct { TotalShares int `json:"totalShares"` TotalComments int `json:"totalComments"` } + +type ArticlePerUserLevelStats struct { + UserLevelID uint `json:"userLevelId"` + UserLevelName string `json:"userLevelName"` + TotalArticle int64 `json:"totalArticle"` +} + +type ArticleMonthlyStats struct { + Year int `json:"year"` + Month int `json:"month"` + View []int `json:"view"` + Comment []int `json:"comment"` + Share []int `json:"share"` +} diff --git a/app/module/articles/service/articles.service.go b/app/module/articles/service/articles.service.go index d760e58..f7df496 100644 --- a/app/module/articles/service/articles.service.go +++ b/app/module/articles/service/articles.service.go @@ -54,6 +54,8 @@ type ArticlesService interface { UpdateActivityCount(id uint, activityTypeId int) (err error) Viewer(c *fiber.Ctx) error SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error) + ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) + ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error) } // NewArticlesService init ArticlesService @@ -400,6 +402,56 @@ func (_i *articlesService) SummaryStats(authToken string) (summaryStats *respons return result, nil } +func (_i *articlesService) ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) { + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("startDate", startDate).Msg("") + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("endDate", endDate).Msg("") + + var userLevelId *uint + var userLevelNumber *int + + if user != nil { + userLevelId = &user.UserLevelId + userLevelNumber = &user.UserLevel.LevelNumber + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("userLevelId", userLevelId).Msg("") + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("userLevelNumber", userLevelNumber).Msg("") + + result, err := _i.Repo.ArticlePerUserLevelStats(userLevelId, userLevelNumber, nil, nil) + if err != nil { + return nil, err + } + return result, nil +} + +func (_i *articlesService) ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error) { + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + var userLevelId *uint + var userLevelNumber *int + + if user != nil { + userLevelId = &user.UserLevelId + userLevelNumber = &user.UserLevel.LevelNumber + } + + result, err := _i.Repo.ArticleMonthlyStats(userLevelId, userLevelNumber, *year) + if err != nil { + return nil, err + } + return result, nil +} + func getFileExtension(filename string) string { // split file name parts := strings.Split(filename, ".") diff --git a/app/module/users/mapper/users.mapper.go b/app/module/users/mapper/users.mapper.go index e48229d..f6878ee 100644 --- a/app/module/users/mapper/users.mapper.go +++ b/app/module/users/mapper/users.mapper.go @@ -1,12 +1,12 @@ package mapper import ( - "go-humas-be/app/database/entity" + "go-humas-be/app/database/entity/users" userLevelsRepository "go-humas-be/app/module/user_levels/repository" res "go-humas-be/app/module/users/response" ) -func UsersResponseMapper(usersReq *entity.Users, userLevelsRepo userLevelsRepository.UserLevelsRepository) (usersRes *res.UsersResponse) { +func UsersResponseMapper(usersReq *users.Users, userLevelsRepo userLevelsRepository.UserLevelsRepository) (usersRes *res.UsersResponse) { if usersReq != nil { findUserLevel, _ := userLevelsRepo.FindOne(usersReq.UserLevelId) userLevelGroup := "" diff --git a/app/module/users/repository/users.repository.go b/app/module/users/repository/users.repository.go index 260346b..208da65 100644 --- a/app/module/users/repository/users.repository.go +++ b/app/module/users/repository/users.repository.go @@ -5,6 +5,7 @@ import ( "github.com/rs/zerolog" "go-humas-be/app/database" "go-humas-be/app/database/entity" + "go-humas-be/app/database/entity/users" "go-humas-be/app/module/users/request" "go-humas-be/utils/paginator" "strings" @@ -17,12 +18,12 @@ type usersRepository struct { // UsersRepository define interface of IUsersRepository type UsersRepository interface { - GetAll(req request.UsersQueryRequest) (userss []*entity.Users, paging paginator.Pagination, err error) - FindOne(id uint) (users *entity.Users, err error) - FindByKeycloakId(keycloakId string) (users *entity.Users, err error) - FindByUsername(username string) (users *entity.Users, err error) - Create(users *entity.Users) (userReturn *entity.Users, err error) - Update(id uint, users *entity.Users) (err error) + GetAll(req request.UsersQueryRequest) (userss []*users.Users, paging paginator.Pagination, err error) + FindOne(id uint) (users *users.Users, err error) + FindByKeycloakId(keycloakId string) (users *users.Users, err error) + FindByUsername(username string) (users *users.Users, err error) + Create(users *users.Users) (userReturn *users.Users, err error) + Update(id uint, users *users.Users) (err error) Delete(id uint) (err error) CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) UpdateForgotPassword(id uint, forgotPasswords *entity.ForgotPasswords) (err error) @@ -39,10 +40,10 @@ func NewUsersRepository(db *database.Database, log zerolog.Logger) UsersReposito } // implement interface of IUsersRepository -func (_i *usersRepository) GetAll(req request.UsersQueryRequest) (userss []*entity.Users, paging paginator.Pagination, err error) { +func (_i *usersRepository) GetAll(req request.UsersQueryRequest) (userss []*users.Users, paging paginator.Pagination, err error) { var count int64 - query := _i.DB.DB.Model(&entity.Users{}) + query := _i.DB.DB.Model(&users.Users{}) query = query.Where("is_active = ?", true) if req.Username != nil && *req.Username != "" { @@ -106,7 +107,7 @@ func (_i *usersRepository) GetAll(req request.UsersQueryRequest) (userss []*enti return } -func (_i *usersRepository) FindOne(id uint) (users *entity.Users, err error) { +func (_i *usersRepository) FindOne(id uint) (users *users.Users, err error) { if err := _i.DB.DB.First(&users, id).Error; err != nil { return nil, err } @@ -114,15 +115,15 @@ func (_i *usersRepository) FindOne(id uint) (users *entity.Users, err error) { return users, nil } -func (_i *usersRepository) FindByKeycloakId(keycloakId string) (users *entity.Users, err error) { - if err := _i.DB.DB.Where("keycloak_id = ?", keycloakId).First(&users).Error; err != nil { +func (_i *usersRepository) FindByKeycloakId(keycloakId string) (users *users.Users, err error) { + if err := _i.DB.DB.Where("keycloak_id = ?", keycloakId).Preload("UserLevel").First(&users).Error; err != nil { return nil, err } return users, nil } -func (_i *usersRepository) FindByUsername(username string) (users *entity.Users, err error) { +func (_i *usersRepository) FindByUsername(username string) (users *users.Users, err error) { if err := _i.DB.DB.Where("username = ?", username).First(&users).Error; err != nil { return nil, err } @@ -130,19 +131,19 @@ func (_i *usersRepository) FindByUsername(username string) (users *entity.Users, return users, nil } -func (_i *usersRepository) Create(users *entity.Users) (userReturn *entity.Users, err error) { +func (_i *usersRepository) Create(users *users.Users) (userReturn *users.Users, err error) { result := _i.DB.DB.Create(users) return users, result.Error } -func (_i *usersRepository) Update(id uint, users *entity.Users) (err error) { - return _i.DB.DB.Model(&entity.Users{}). - Where(&entity.Users{ID: id}). - Updates(users).Error +func (_i *usersRepository) Update(id uint, usersReturn *users.Users) (err error) { + return _i.DB.DB.Model(&users.Users{}). + Where(&users.Users{ID: id}). + Updates(usersReturn).Error } func (_i *usersRepository) Delete(id uint) error { - return _i.DB.DB.Delete(&entity.Users{}, id).Error + return _i.DB.DB.Delete(&users.Users{}, id).Error } func (_i *usersRepository) CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) { diff --git a/app/module/users/request/users.request.go b/app/module/users/request/users.request.go index a8444a2..6d6dc8b 100644 --- a/app/module/users/request/users.request.go +++ b/app/module/users/request/users.request.go @@ -1,7 +1,7 @@ package request import ( - "go-humas-be/app/database/entity" + "go-humas-be/app/database/entity/users" "go-humas-be/utils/paginator" "strconv" "time" @@ -46,8 +46,8 @@ type UsersCreateRequest struct { LastEducation *string `json:"lastEducation"` } -func (req UsersCreateRequest) ToEntity() *entity.Users { - return &entity.Users{ +func (req UsersCreateRequest) ToEntity() *users.Users { + return &users.Users{ Username: req.Username, Email: req.Email, Fullname: req.Fullname, @@ -85,8 +85,8 @@ type UsersUpdateRequest struct { StatusId *int `json:"statusId"` } -func (req UsersUpdateRequest) ToEntity() *entity.Users { - return &entity.Users{ +func (req UsersUpdateRequest) ToEntity() *users.Users { + return &users.Users{ Username: req.Username, Email: req.Email, Fullname: req.Fullname, diff --git a/app/module/users/service/users.service.go b/app/module/users/service/users.service.go index 8422854..b7e2951 100644 --- a/app/module/users/service/users.service.go +++ b/app/module/users/service/users.service.go @@ -8,6 +8,7 @@ import ( "github.com/Nerzal/gocloak/v13" "github.com/rs/zerolog" "go-humas-be/app/database/entity" + "go-humas-be/app/database/entity/users" userLevelsRepository "go-humas-be/app/module/user_levels/repository" "go-humas-be/app/module/users/mapper" "go-humas-be/app/module/users/repository" @@ -35,7 +36,7 @@ type UsersService interface { Show(id uint) (users *response.UsersResponse, err error) ShowByUsername(username string) (users *response.UsersResponse, err error) ShowUserInfo(authToken string) (users *response.UsersResponse, err error) - Save(req request.UsersCreateRequest, authToken string) (userReturn *entity.Users, err error) + Save(req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error) Login(req request.UserLogin) (res *gocloak.JWT, err error) ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) Update(id uint, req request.UsersUpdateRequest) (err error) @@ -97,7 +98,7 @@ func (_i *usersService) ShowUserInfo(authToken string) (users *response.UsersRes return mapper.UsersResponseMapper(userInfo, _i.UserLevelsRepo), nil } -func (_i *usersService) Save(req request.UsersCreateRequest, authToken string) (userReturn *entity.Users, err error) { +func (_i *usersService) Save(req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error) { _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() diff --git a/config/config/index.config.go b/config/config/index.config.go index 06745ef..c20cd45 100644 --- a/config/config/index.config.go +++ b/config/config/index.config.go @@ -30,6 +30,7 @@ type app = struct { type db = struct { Postgres struct { DSN string `toml:"dsn"` + LogMode string `toml:"log-mode"` Migrate bool `toml:"migrate"` Seed bool `toml:"seed"` } diff --git a/config/toml/config.toml b/config/toml/config.toml index fce2115..57b179b 100644 --- a/config/toml/config.toml +++ b/config/toml/config.toml @@ -13,6 +13,7 @@ body-limit = 1048576000 # "100 * 1024 * 1024" [db.postgres] dsn = "postgresql://humas_user:HumasDB@2024@38.47.180.165:5432/humas_db" # ://:@:/ +log-mode = "NONE" migrate = false seed = false diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 57d94ff..c986c16 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -2339,6 +2339,62 @@ const docTemplate = `{ } } }, + "/articles/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticleMonthlyStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticleMonthlyStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/articles/statistic/summary": { "get": { "security": [ @@ -2389,6 +2445,68 @@ const docTemplate = `{ } } }, + "/articles/statistic/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticlePerUserLevelStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticlePerUserLevelStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "start date", + "name": "startDate", + "in": "query" + }, + { + "type": "string", + "description": "start date", + "name": "endDate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/articles/thumbnail/viewer/{thumbnailName}": { "get": { "security": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 931df61..422cf9e 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -2328,6 +2328,62 @@ } } }, + "/articles/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticleMonthlyStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticleMonthlyStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/articles/statistic/summary": { "get": { "security": [ @@ -2378,6 +2434,68 @@ } } }, + "/articles/statistic/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticlePerUserLevelStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticlePerUserLevelStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "start date", + "name": "startDate", + "in": "query" + }, + { + "type": "string", + "description": "start date", + "name": "endDate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/articles/thumbnail/viewer/{thumbnailName}": { "get": { "security": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index b752561..34f39d1 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -2354,6 +2354,42 @@ paths: summary: Update Articles tags: - Articles + /articles/statistic/monthly: + get: + description: API for ArticleMonthlyStats of Article + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + required: true + type: string + - description: year + in: query + name: year + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ArticleMonthlyStats Articles + tags: + - Articles /articles/statistic/summary: get: description: API for Summary Stats of Article @@ -2386,6 +2422,46 @@ paths: summary: SummaryStats Articles tags: - Articles + /articles/statistic/user-levels: + get: + description: API for ArticlePerUserLevelStats of Article + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + required: true + type: string + - description: start date + in: query + name: startDate + type: string + - description: start date + in: query + name: endDate + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ArticlePerUserLevelStats Articles + tags: + - Articles /articles/thumbnail/{id}: post: description: API for Save Thumbnail of Articles diff --git a/utils/service/user_utils.service.go b/utils/service/user_utils.service.go index 863d57d..bdb1b79 100644 --- a/utils/service/user_utils.service.go +++ b/utils/service/user_utils.service.go @@ -3,13 +3,13 @@ package service import ( "github.com/golang-jwt/jwt/v5" "github.com/rs/zerolog" - "go-humas-be/app/database/entity" + "go-humas-be/app/database/entity/users" "go-humas-be/app/module/users/repository" "strings" "time" ) -func GetUserInfo(log zerolog.Logger, repo repository.UsersRepository, bearerToken string) *entity.Users { +func GetUserInfo(log zerolog.Logger, repo repository.UsersRepository, bearerToken string) *users.Users { tokenString := strings.TrimPrefix(bearerToken, "Bearer ") token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) if err != nil {