feat: add article stats
This commit is contained in:
parent
4fb34ef46c
commit
6b8ab00e3b
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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()"`
|
||||
}
|
||||
|
|
@ -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()"`
|
||||
}
|
||||
|
|
@ -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{},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <Add access token here>)
|
||||
// @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 <Add access token here>)
|
||||
// @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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ".")
|
||||
|
|
|
|||
|
|
@ -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 := ""
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" # <driver>://<username>:<password>@<host>:<port>/<database>
|
||||
log-mode = "NONE"
|
||||
migrate = false
|
||||
seed = false
|
||||
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -2354,6 +2354,42 @@ paths:
|
|||
summary: Update Articles
|
||||
tags:
|
||||
- Articles
|
||||
/articles/statistic/monthly:
|
||||
get:
|
||||
description: API for ArticleMonthlyStats of Article
|
||||
parameters:
|
||||
- default: Bearer <Add access token here>
|
||||
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 <Add access token here>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue