Files
artalkjs/server/handler/comment_get_query.go

244 lines
5.9 KiB
Go

package handler
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/ArtalkJS/Artalk/internal/core"
"github.com/ArtalkJS/Artalk/internal/entity"
"github.com/ArtalkJS/Artalk/server/common"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
// 获取评论查询实例
func GetCommentQuery(app *core.App, c *fiber.Ctx, p ParamsGet, siteID uint, scopes ...func(*gorm.DB) *gorm.DB) *gorm.DB {
q := app.Dao().DB().Model(&entity.Comment{})
q.Scopes(SiteIsolationScope(app, c, p), AllowedComment(app, c, p))
q.Order(GetSortRuleSQL(p.SortBy, "created_at DESC")) // 排序规则
q.Scopes(scopes...)
switch {
case !p.IsMsgCenter:
// ==========
// 非通知中心
// ==========
// 只看管理员功能
if p.ViewOnlyAdmin {
adminIDs := app.Dao().GetAllAdminIDs() // 获取管理员列表
q.Where("user_id IN ?", adminIDs) // 只允许管理员 user_id
}
q.Where("page_key = ?", p.PageKey)
case p.IsMsgCenter:
// ==========
// 通知中心
// ==========
user := p.User
if user == nil || user.IsEmpty() {
return q.Where("id = 0") // user not found
}
// admin_only 检测
if strings.HasPrefix(p.Type, "admin_") {
if !p.IsAdminReq || !common.IsAdminHasSiteAccess(app, c, p.SiteName) {
return q.Where("id = 0")
}
}
switch p.Type {
case "all":
q.Where("rid IN (?) OR user_id = ?", app.Dao().GetUserAllCommentIDs(user.ID), user.ID)
case "mentions":
q.Where("rid IN (?) AND user_id != ?", app.Dao().GetUserAllCommentIDs(user.ID), user.ID)
case "mine":
q.Where("user_id = ?", user.ID)
case "pending":
q.Where("user_id = ? AND is_pending = ?", user.ID, true)
case "admin_all":
case "admin_pending":
q.Where("is_pending = ?", true)
default:
return q.Where("id = 0")
}
}
return q
}
// 根评论 Root comments
func RootComments() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("rid = 0")
}
}
// 允许的评论
func AllowedComment(app *core.App, c *fiber.Ctx, p ParamsGet) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if common.CheckIsAdminReq(app, c) {
return db // 管理员显示全部
}
// 通知中心允许显示个人的待审状态的评论
if p.IsMsgCenter && !p.User.IsEmpty() {
return db.Where("is_pending = ? OR (is_pending = ? AND user_id = ?)", false, true, p.User.ID)
}
return db.Where("is_pending = ?", false) // 不允许待审评论
}
}
func AllowedCommentChecker(app *core.App, c *fiber.Ctx, p ParamsGet) func(*entity.Comment) bool {
return func(comment *entity.Comment) bool {
if common.CheckIsAdminReq(app, c) {
return true // 管理员显示全部
}
// 通知中心允许显示个人的待审状态的评论
if p.IsMsgCenter && p.User.ID == comment.UserID {
return true
}
// 不允许待审评论
if comment.IsPending {
return false
}
return true
}
}
func SiteIsolationChecker(app *core.App, c *fiber.Ctx, p ParamsGet) func(*entity.Comment) bool {
return func(comment *entity.Comment) bool {
if common.CheckIsAdminReq(app, c) && p.SiteAll {
return true // 仅管理员支持取消站点隔离
}
if comment.SiteName != p.SiteName {
return false
}
return true
}
}
// 站点隔离
func SiteIsolationScope(app *core.App, c *fiber.Ctx, p ParamsGet) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if common.CheckIsAdminReq(app, c) && p.SiteAll {
return db // 仅管理员支持取消站点隔离
}
return db.Where("site_name = ?", p.SiteName)
}
}
// 排序规则
func GetSortRuleSQL(sortBy string, defaultSQL string) string {
switch sortBy {
case "date_desc":
return "created_at DESC"
case "date_asc":
return "created_at ASC"
case "vote":
return "vote_up DESC, created_at DESC"
}
return defaultSQL
}
// 评论计数
func CountComments(db *gorm.DB) int64 {
var count int64
db.Count(&count)
return count
}
// 评论搜索
func CommentSearchScope(app *core.App, p ParamsGet) func(d *gorm.DB) *gorm.DB {
var userIds []uint
app.Dao().DB().Model(&entity.User{}).Where(
"LOWER(name) = LOWER(?) OR LOWER(email) = LOWER(?)", p.Search, p.Search,
).Pluck("id", &userIds)
return func(d *gorm.DB) *gorm.DB {
return d.Where("user_id IN (?) OR content LIKE ? OR page_key = ? OR ip = ? OR ua = ?",
userIds, "%"+p.Search+"%", p.Search, p.Search, p.Search)
}
}
// 分页
func Paginate(offset int, limit int) func(db *gorm.DB) *gorm.DB {
if offset < 0 {
offset = 0
}
if limit > 100 {
limit = 100
} else if limit <= 0 {
limit = 15
}
return func(db *gorm.DB) *gorm.DB {
return db.Offset(offset).Limit(limit)
}
}
// 插入置顶的评论
func prependPinnedComments(app *core.App, c *fiber.Ctx, p ParamsGet, comments *[]entity.Comment) {
if p.IsMsgCenter || p.Offset != 0 {
return // 通知中心关闭置顶 & 仅在分页的首页加入置顶评论
}
pinnedComments := []entity.Comment{}
GetCommentQuery(app, c, p, p.SiteID).Where("is_pinned = ?", true).Find(&pinnedComments)
if len(pinnedComments) == 0 {
return // 没有置顶评论
}
// 去掉已 pinned 且重复存在于原列表中的评论
filteredComments := []entity.Comment{}
for _, co := range *comments {
if !entity.ContainsComment(pinnedComments, co.ID) {
filteredComments = append(filteredComments, co)
}
}
// prepend
*comments = append(pinnedComments, filteredComments...)
}
// 处理来自配置文件的 fronted 配置项
func UseCfgFrontend(app *core.App, p *ParamsGet) {
feConf := app.Conf().Frontend
if feConf == nil || reflect.ValueOf(feConf).Kind() != reflect.Map {
return
}
// pagination
// TODO consider ignore pageSize configuration in sidebar
(func() {
pagination, isExist := feConf["pagination"]
if !isExist {
return
}
if reflect.ValueOf(pagination).Kind() != reflect.Map {
return
}
// pagination.pageSize
cfgPageSizeStr := fmt.Sprintf("%v", pagination.(common.Map)["pageSize"])
confPageSize, err := strconv.Atoi(cfgPageSizeStr)
if err == nil && confPageSize > 0 {
p.Limit = confPageSize
}
})()
}