feat: update article and stats
This commit is contained in:
parent
6f16cb9279
commit
4fb34ef46c
|
|
@ -6,7 +6,7 @@ type ActivityLogs struct {
|
|||
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
|
||||
ActivityTypeId int `json:"activity_type_id" gorm:"type:int4"`
|
||||
Url string `json:"url" gorm:"type:varchar"`
|
||||
ArticleId *int `json:"article_id" gorm:"type:int4"`
|
||||
ArticleId *uint `json:"article_id" gorm:"type:int4"`
|
||||
UserId *uint `json:"user_id" gorm:"type:int4"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ type Articles struct {
|
|||
ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"`
|
||||
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"`
|
||||
AiArticleId *int `json:"ai_article_id" gorm:"type:int4"`
|
||||
DownloadCount *int `json:"download_count" gorm:"type:int4"`
|
||||
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"`
|
||||
|
|
|
|||
|
|
@ -25,11 +25,6 @@ var activityLogTypes = []entity.ActivityLogTypes{
|
|||
},
|
||||
{
|
||||
ID: 4,
|
||||
Name: "Like",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
ID: 5,
|
||||
Name: "Comment",
|
||||
IsActive: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type ActivityLogsQueryRequest struct {
|
|||
type ActivityLogsCreateRequest struct {
|
||||
ActivityTypeId int `json:"activityTypeId" validate:"required"`
|
||||
Url string `json:"url" validate:"required"`
|
||||
ArticleId *int `json:"articleId"`
|
||||
ArticleId *uint `json:"articleId"`
|
||||
UserId *uint `json:"userId"`
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ type ActivityLogsUpdateRequest struct {
|
|||
ID uint `json:"id" validate:"required"`
|
||||
ActivityTypeId int `json:"activityTypeId" validate:"required"`
|
||||
Url string `json:"url" validate:"required"`
|
||||
ArticleId *int `json:"articleId"`
|
||||
ArticleId *uint `json:"articleId"`
|
||||
UserId *uint `json:"userId"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ type ActivityLogsResponse struct {
|
|||
ID uint `json:"id"`
|
||||
ActivityTypeId int `json:"activityTypeId"`
|
||||
Url string `json:"url"`
|
||||
ArticleId *int `json:"articleId"`
|
||||
ArticleId *uint `json:"articleId"`
|
||||
UserId *uint `json:"userId"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"go-humas-be/app/module/activity_logs/repository"
|
||||
"go-humas-be/app/module/activity_logs/request"
|
||||
"go-humas-be/app/module/activity_logs/response"
|
||||
"go-humas-be/app/module/articles/service"
|
||||
usersRepository "go-humas-be/app/module/users/repository"
|
||||
"go-humas-be/utils/paginator"
|
||||
utilSvc "go-humas-be/utils/service"
|
||||
|
|
@ -14,9 +15,10 @@ import (
|
|||
|
||||
// ActivityLogsService
|
||||
type activityLogsService struct {
|
||||
Repo repository.ActivityLogsRepository
|
||||
UsersRepo usersRepository.UsersRepository
|
||||
Log zerolog.Logger
|
||||
Repo repository.ActivityLogsRepository
|
||||
UsersRepo usersRepository.UsersRepository
|
||||
ArticleService service.ArticlesService
|
||||
Log zerolog.Logger
|
||||
}
|
||||
|
||||
// ActivityLogsService define interface of IActivityLogsService
|
||||
|
|
@ -29,12 +31,13 @@ type ActivityLogsService interface {
|
|||
}
|
||||
|
||||
// NewActivityLogsService init ActivityLogsService
|
||||
func NewActivityLogsService(repo repository.ActivityLogsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) ActivityLogsService {
|
||||
func NewActivityLogsService(repo repository.ActivityLogsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository, articleService service.ArticlesService) ActivityLogsService {
|
||||
|
||||
return &activityLogsService{
|
||||
Repo: repo,
|
||||
Log: log,
|
||||
UsersRepo: usersRepo,
|
||||
Repo: repo,
|
||||
Log: log,
|
||||
UsersRepo: usersRepo,
|
||||
ArticleService: articleService,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +74,18 @@ func (_i *activityLogsService) Save(req request.ActivityLogsCreateRequest, authT
|
|||
newReq.UserId = &createdBy.ID
|
||||
}
|
||||
|
||||
return _i.Repo.Create(newReq)
|
||||
result, err := _i.Repo.Create(newReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update article
|
||||
err = _i.ArticleService.UpdateActivityCount(*req.ArticleId, req.ActivityTypeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (_i *activityLogsService) Update(id uint, req request.ActivityLogsUpdateRequest) (err error) {
|
||||
|
|
|
|||
|
|
@ -51,5 +51,6 @@ func (_i *ArticlesRouter) RegisterArticlesRoutes() {
|
|||
router.Post("/thumbnail/:id", articlesController.SaveThumbnail)
|
||||
router.Get("/thumbnail/viewer/:thumbnailName", articlesController.Viewer)
|
||||
router.Delete("/:id", articlesController.Delete)
|
||||
router.Get("/statistic/summary", articlesController.SummaryStats)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ type ArticlesController interface {
|
|||
Update(c *fiber.Ctx) error
|
||||
Delete(c *fiber.Ctx) error
|
||||
Viewer(c *fiber.Ctx) error
|
||||
SummaryStats(c *fiber.Ctx) error
|
||||
}
|
||||
|
||||
func NewArticlesController(articlesService service.ArticlesService) ArticlesController {
|
||||
|
|
@ -237,3 +238,29 @@ func (_i *articlesController) Delete(c *fiber.Ctx) error {
|
|||
func (_i *articlesController) Viewer(c *fiber.Ctx) error {
|
||||
return _i.articlesService.Viewer(c)
|
||||
}
|
||||
|
||||
// SummaryStats Articles
|
||||
// @Summary SummaryStats Articles
|
||||
// @Description API for Summary Stats of Article
|
||||
// @Tags Articles
|
||||
// @Security Bearer
|
||||
// @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /articles/statistic/summary [get]
|
||||
func (_i *articlesController) SummaryStats(c *fiber.Ctx) error {
|
||||
authToken := c.Get("Authorization")
|
||||
|
||||
response, err := _i.articlesService.SummaryStats(authToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"Summary Stats of Articles successfully retrieved"},
|
||||
Data: response,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ func ArticlesResponseMapper(
|
|||
CreatedByName: &createdByName,
|
||||
ShareCount: articlesReq.ShareCount,
|
||||
ViewCount: articlesReq.ViewCount,
|
||||
DownloadCount: articlesReq.DownloadCount,
|
||||
CommentCount: articlesReq.CommentCount,
|
||||
StatusId: articlesReq.StatusId,
|
||||
IsPublish: articlesReq.IsPublish,
|
||||
PublishedAt: articlesReq.PublishedAt,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import (
|
|||
"go-humas-be/app/database"
|
||||
"go-humas-be/app/database/entity"
|
||||
"go-humas-be/app/module/articles/request"
|
||||
"go-humas-be/app/module/articles/response"
|
||||
"go-humas-be/utils/paginator"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type articlesRepository struct {
|
||||
|
|
@ -23,6 +25,7 @@ type ArticlesRepository interface {
|
|||
Create(articles *entity.Articles) (articleReturn *entity.Articles, err error)
|
||||
Update(id uint, articles *entity.Articles) (err error)
|
||||
Delete(id uint) (err error)
|
||||
SummaryStats(userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error)
|
||||
}
|
||||
|
||||
func NewArticlesRepository(db *database.Database, log zerolog.Logger) ArticlesRepository {
|
||||
|
|
@ -128,3 +131,25 @@ func (_i *articlesRepository) Update(id uint, articles *entity.Articles) (err er
|
|||
func (_i *articlesRepository) Delete(id uint) error {
|
||||
return _i.DB.DB.Delete(&entity.Articles{}, id).Error
|
||||
}
|
||||
|
||||
func (_i *articlesRepository) SummaryStats(userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) {
|
||||
now := time.Now()
|
||||
startOfDay := now.Truncate(24 * time.Hour)
|
||||
startOfWeek := now.AddDate(0, 0, -int(now.Weekday())+1).Truncate(24 * time.Hour)
|
||||
|
||||
// Query
|
||||
err = _i.DB.DB.Model(&entity.Articles{}).
|
||||
Select(
|
||||
"COUNT(*) AS total_all, "+
|
||||
"COALESCE(SUM(view_count), 0) AS total_views, "+
|
||||
"COALESCE(SUM(share_count), 0) AS total_shares, "+
|
||||
"COALESCE(SUM(comment_count), 0) AS total_comments, "+
|
||||
"COUNT(CASE WHEN created_at >= ? THEN 1 END) AS total_today, "+
|
||||
"COUNT(CASE WHEN created_at >= ? THEN 1 END) AS total_this_week",
|
||||
startOfDay, startOfWeek,
|
||||
).
|
||||
Where("created_by_id = ?", userID).
|
||||
Scan(&articleSummaryStats).Error
|
||||
|
||||
return articleSummaryStats, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type ArticlesResponse struct {
|
|||
CreatedByName *string `json:"createdByName"`
|
||||
ShareCount *int `json:"shareCount"`
|
||||
ViewCount *int `json:"viewCount"`
|
||||
DownloadCount *int `json:"downloadCount"`
|
||||
CommentCount *int `json:"commentCount"`
|
||||
AiArticleId *int `json:"aiArticleId"`
|
||||
StatusId *int `json:"statusId"`
|
||||
IsPublish *bool `json:"isPublish"`
|
||||
|
|
@ -34,3 +34,12 @@ type ArticlesResponse struct {
|
|||
ArticleFiles []*articleFilesResponse.ArticleFilesResponse `json:"files"`
|
||||
ArticleCategories []*articleCategoriesResponse.ArticleCategoriesResponse `json:"categories"`
|
||||
}
|
||||
|
||||
type ArticleSummaryStats struct {
|
||||
TotalToday int `json:"totalToday"`
|
||||
TotalThisWeek int `json:"totalThisWeek"`
|
||||
TotalAll int `json:"totalAll"`
|
||||
TotalViews int `json:"totalViews"`
|
||||
TotalShares int `json:"totalShares"`
|
||||
TotalComments int `json:"totalComments"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ type ArticlesService interface {
|
|||
SaveThumbnail(c *fiber.Ctx) (err error)
|
||||
Update(id uint, req request.ArticlesUpdateRequest) (err error)
|
||||
Delete(id uint) error
|
||||
UpdateActivityCount(id uint, activityTypeId int) (err error)
|
||||
Viewer(c *fiber.Ctx) error
|
||||
SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error)
|
||||
}
|
||||
|
||||
// NewArticlesService init ArticlesService
|
||||
|
|
@ -356,6 +358,48 @@ func (_i *articlesService) Viewer(c *fiber.Ctx) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (_i *articlesService) UpdateActivityCount(id uint, activityTypeId int) error {
|
||||
result, err := _i.Repo.FindOne(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viewCount := 0
|
||||
if result.ViewCount != nil {
|
||||
viewCount = *result.ViewCount
|
||||
}
|
||||
shareCount := 0
|
||||
if result.ShareCount != nil {
|
||||
shareCount = *result.ShareCount
|
||||
}
|
||||
commentCount := 0
|
||||
if result.CommentCount != nil {
|
||||
commentCount = *result.CommentCount
|
||||
}
|
||||
|
||||
if activityTypeId == 2 {
|
||||
viewCount++
|
||||
} else if activityTypeId == 3 {
|
||||
shareCount++
|
||||
} else if activityTypeId == 4 {
|
||||
commentCount++
|
||||
}
|
||||
result.ViewCount = &viewCount
|
||||
result.ShareCount = &shareCount
|
||||
result.CommentCount = &commentCount
|
||||
return _i.Repo.Update(id, result)
|
||||
}
|
||||
|
||||
func (_i *articlesService) SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error) {
|
||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||
|
||||
result, err := _i.Repo.SummaryStats(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getFileExtension(filename string) string {
|
||||
// split file name
|
||||
parts := strings.Split(filename, ".")
|
||||
|
|
|
|||
|
|
@ -2339,6 +2339,56 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/articles/statistic/summary": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for Summary Stats of Article",
|
||||
"tags": [
|
||||
"Articles"
|
||||
],
|
||||
"summary": "SummaryStats Articles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "Bearer \u003cAdd access token here\u003e",
|
||||
"description": "Insert your access token",
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"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,56 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/articles/statistic/summary": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for Summary Stats of Article",
|
||||
"tags": [
|
||||
"Articles"
|
||||
],
|
||||
"summary": "SummaryStats Articles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "Bearer \u003cAdd access token here\u003e",
|
||||
"description": "Insert your access token",
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"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,38 @@ paths:
|
|||
summary: Update Articles
|
||||
tags:
|
||||
- Articles
|
||||
/articles/statistic/summary:
|
||||
get:
|
||||
description: API for Summary Stats of Article
|
||||
parameters:
|
||||
- default: Bearer <Add access token here>
|
||||
description: Insert your access token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
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: SummaryStats Articles
|
||||
tags:
|
||||
- Articles
|
||||
/articles/thumbnail/{id}:
|
||||
post:
|
||||
description: API for Save Thumbnail of Articles
|
||||
|
|
|
|||
Loading…
Reference in New Issue