Count post views

This commit is contained in:
LittleSheep 2025-02-17 17:21:15 +08:00
parent 14c17eded8
commit 269e79ca58
11 changed files with 141 additions and 20 deletions

View File

@ -15,6 +15,7 @@ var AutoMaintainRange = []any{
&models.Poll{},
&models.PollAnswer{},
&models.PostFlag{},
&models.PostView{},
}
func RunMigration(source *gorm.DB) error {

View File

@ -2,13 +2,14 @@ package api
import (
"fmt"
"time"
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
"git.solsynth.dev/hypernet/interactive/pkg/internal/services"
vocab "github.com/go-ap/activitypub"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
"time"
)
func apGetPublisher(c *fiber.Ctx) error {
@ -42,7 +43,7 @@ func apGetPost(c *fiber.Ctx) error {
return err
}
items, err := services.ListPost(tx, take, offset, "published_at DESC")
items, err := services.ListPost(tx, take, offset, "published_at DESC", nil)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -2,13 +2,14 @@ package api
import (
"fmt"
"strconv"
"strings"
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
"git.solsynth.dev/hypernet/passport/pkg/authkit"
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"gorm.io/gorm"
"strconv"
"strings"
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
"git.solsynth.dev/hypernet/interactive/pkg/internal/gap"
@ -114,13 +115,18 @@ func searchPost(c *fiber.Ctx) error {
return err
}
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
countTx := tx
count, err := services.CountPost(countTx)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
items, err := services.ListPost(tx, take, offset, "published_at DESC")
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -150,13 +156,18 @@ func listPost(c *fiber.Ctx) error {
return err
}
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
countTx := tx
count, err := services.CountPost(countTx)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
items, err := services.ListPost(tx, take, offset, "published_at DESC")
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -222,12 +233,17 @@ func listDraftPost(c *fiber.Ctx) error {
tx := services.FilterPostWithAuthorDraft(database.C, user.ID)
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
count, err := services.CountPost(tx)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
items, err := services.ListPost(tx, take, offset, "created_at DESC", true)
items, err := services.ListPost(tx, take, offset, "created_at DESC", userId, true)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -28,7 +28,12 @@ func listPinnedPost(c *fiber.Ctx) error {
tx = tx.Where("publisher_id = ?", user.ID)
tx = tx.Where("pinned_at IS NOT NULL")
items, err := services.ListPost(tx, 100, 0, "published_at DESC")
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
items, err := services.ListPost(tx, 100, 0, "published_at DESC", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -25,8 +25,13 @@ func listRecommendation(c *fiber.Ctx) error {
return item.ID
})
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
tx := database.C.Where("id IN ?", postIdx)
newPosts, err := services.ListPost(tx, featuredMax, 0, "id ASC")
newPosts, err := services.ListPost(tx, featuredMax, 0, "id ASC", userId)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
@ -65,6 +70,11 @@ func listRecommendationFriends(c *fiber.Ctx) error {
tx = tx.Where("publisher_id IN ?", friendList)
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
countTx := tx
count, err := services.CountPost(countTx)
if err != nil {
@ -76,7 +86,7 @@ func listRecommendationFriends(c *fiber.Ctx) error {
order = "published_at DESC, (COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC"
}
items, err := services.ListPost(tx, take, offset, order)
items, err := services.ListPost(tx, take, offset, order, userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -106,13 +116,18 @@ func listRecommendationShuffle(c *fiber.Ctx) error {
return err
}
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
countTx := tx
count, err := services.CountPost(countTx)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
items, err := services.ListPost(tx, take, offset, "RANDOM()")
items, err := services.ListPost(tx, take, offset, "RANDOM()", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -2,9 +2,11 @@ package api
import (
"fmt"
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
"git.solsynth.dev/hypernet/interactive/pkg/internal/services"
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"github.com/gofiber/fiber/v2"
)
@ -35,12 +37,17 @@ func listPostReplies(c *fiber.Ctx) error {
tx = services.FilterPostWithTag(tx, c.Query("tag"))
}
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
count, err := services.CountPost(tx)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
items, err := services.ListPost(tx, take, offset, "published_at DESC")
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -55,6 +62,11 @@ func listPostFeaturedReply(c *fiber.Ctx) error {
take := c.QueryInt("take", 0)
take = max(1, min(take, 3))
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
tx := database.C
var post models.Post
if err := database.C.Where("id = ?", c.Params("postId")).First(&post).Error; err != nil {
@ -78,7 +90,7 @@ func listPostFeaturedReply(c *fiber.Ctx) error {
tx = services.FilterPostWithTag(tx, c.Query("tag"))
}
items, err := services.ListPost(tx, take, 0, "(COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC, published_at DESC")
items, err := services.ListPost(tx, take, 0, "(COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC, published_at DESC", userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -24,6 +24,11 @@ func getWhatsNew(c *fiber.Ctx) error {
tx = tx.Where("id > ?", pivot)
var userId *uint
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
userId = &user.ID
}
countTx := tx
count, err := services.CountPost(countTx)
if err != nil {
@ -35,7 +40,7 @@ func getWhatsNew(c *fiber.Ctx) error {
order = "published_at DESC, (COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC"
}
items, err := services.ListPost(tx, 10, 0, order)
items, err := services.ListPost(tx, 10, 0, order, userId)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

View File

@ -56,8 +56,10 @@ type Post struct {
PublishedAt *time.Time `json:"published_at"`
PublishedUntil *time.Time `json:"published_until"`
TotalUpvote int `json:"total_upvote"`
TotalDownvote int `json:"total_downvote"`
TotalUpvote int `json:"total_upvote"`
TotalDownvote int `json:"total_downvote"`
TotalViews int64 `json:"total_views"`
TotalAggressiveViews int64 `json:"total_aggressive_views"`
PollID *uint `json:"poll_id"`
Poll *Poll `json:"poll"`
@ -111,3 +113,10 @@ type PostInsight struct {
Post Post `json:"post"`
PostID uint `json:"post_id"`
}
type PostView struct {
AccountID uint `json:"account_id" gorm:"primaryKey"`
PostID uint `json:"post_id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

View File

@ -0,0 +1,49 @@
package services
import (
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
"gorm.io/gorm"
)
var postViewQueue []models.PostView
func AddPostView(post models.Post, account uint) {
postViewQueue = append(postViewQueue, models.PostView{
AccountID: account,
PostID: post.ID,
})
}
func AddPostViews(posts []models.Post, account uint) {
for _, post := range posts {
postViewQueue = append(postViewQueue, models.PostView{
AccountID: account,
PostID: post.ID,
})
}
}
func FlushPostViews() {
if len(postViewQueue) == 0 {
return
}
workingQueue := make([]models.PostView, len(postViewQueue))
copy(workingQueue, postViewQueue)
clear(postViewQueue)
updateRequiredPost := make(map[uint]bool)
for _, item := range workingQueue {
updateRequiredPost[item.PostID] = true
}
_ = database.C.CreateInBatches(workingQueue, 1000).Error
for k := range updateRequiredPost {
var count int64
if err := database.C.Model(&models.PostView{}).Where("post_id = ?", k).Count(&count).Error; err != nil {
continue
}
database.C.Model(&models.Post{}).Where("id = ?", k).Updates(map[string]any{
"total_views": count,
"total_aggressive_views": gorm.Expr("total_aggressive_views + ?", count),
})
}
}

View File

@ -262,7 +262,7 @@ func CountPostReactions(id uint) int64 {
return count
}
func ListPost(tx *gorm.DB, take int, offset int, order any, noReact ...bool) ([]*models.Post, error) {
func ListPost(tx *gorm.DB, take int, offset int, order any, user *uint, noReact ...bool) ([]*models.Post, error) {
if take > 100 {
take = 100
}
@ -332,6 +332,12 @@ func ListPost(tx *gorm.DB, take int, offset int, order any, noReact ...bool) ([]
}
}
if user != nil {
AddPostViews(lo.Map(items, func(item *models.Post, index int) models.Post {
return *item
}), *user)
}
return items, nil
}

View File

@ -2,14 +2,15 @@ package main
import (
"fmt"
"os"
"os/signal"
"syscall"
pkg "git.solsynth.dev/hypernet/interactive/pkg/internal"
"git.solsynth.dev/hypernet/interactive/pkg/internal/cache"
"git.solsynth.dev/hypernet/interactive/pkg/internal/gap"
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
"github.com/fatih/color"
"os"
"os/signal"
"syscall"
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
"git.solsynth.dev/hypernet/interactive/pkg/internal/grpc"
@ -67,6 +68,7 @@ func main() {
// Configure timed tasks
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
quartz.AddFunc("@every 1m", services.FlushPostViews)
quartz.Start()
// Initialize cache