Files
web_hghl_server/app/service/moreser/mixed.go
T
2026-10-26 16:28:14 +08:00

138 lines
4.1 KiB
Go

package moreser
// NormalizePage 规范化分页参数,限制单次请求数量避免大分页拖垮 MongoDB。
func NormalizePage(pageNum, pageSize, defaultPageSize, maxPageSize int64) (int64, int64) {
if pageNum <= 0 {
pageNum = 1
}
if pageSize <= 0 {
pageSize = defaultPageSize
}
if pageSize > maxPageSize {
pageSize = maxPageSize
}
return pageNum, pageSize
}
// InsertedAdsCount 计算实际可穿插的广告数量,每 interval 条内容后插入 1 条广告。
func InsertedAdsCount(contentTotal, adsTotal, interval int64) int64 {
if contentTotal <= 0 || adsTotal <= 0 || interval <= 0 {
return 0
}
insertSlots := contentTotal / interval
if insertSlots > adsTotal {
return adsTotal
}
return insertSlots
}
// ContentPosition 计算内容原始下标在混排列表中的全局位置。
func ContentPosition(contentIndex, adsTotal, interval int64) int64 {
if interval <= 0 {
return contentIndex
}
adsBefore := contentIndex / interval
if adsBefore > adsTotal {
adsBefore = adsTotal
}
return contentIndex + adsBefore
}
// FirstContentIndexAtOrAfter 找到指定混排位置之后第一条内容下标。
func FirstContentIndexAtOrAfter(pos, contentTotal, adsTotal, interval int64) int64 {
left, right := int64(0), contentTotal
for left < right {
mid := left + (right-left)/2
if ContentPosition(mid, adsTotal, interval) < pos {
left = mid + 1
continue
}
right = mid
}
return left
}
// ContentRange 计算当前混排分页需要读取的内容下标范围。
func ContentRange(start, end, contentTotal, adsTotal, interval int64) (int64, int64) {
contentStart := FirstContentIndexAtOrAfter(start, contentTotal, adsTotal, interval)
contentEnd := FirstContentIndexAtOrAfter(end, contentTotal, adsTotal, interval)
if contentStart >= contentEnd {
return 0, 0
}
return contentStart, contentEnd - contentStart
}
// ContentRangeWithoutTotal 计算当前混排分页需要读取的内容范围,不依赖内容总数。
// 适用于列表接口不返回 totalCount 的场景;广告位仍按“每 interval 条内容后插入 1 条广告”计算。
func ContentRangeWithoutTotal(start, end, adsTotal, interval int64) (int64, int64) {
if start < 0 || end <= start {
return 0, 0
}
contentStart := contentIndexAtMixedPosition(start, adsTotal, interval)
contentEnd := contentIndexAtMixedPosition(end, adsTotal, interval)
if contentStart >= contentEnd {
return 0, 0
}
return contentStart, contentEnd - contentStart
}
// ContentIndexAtPositionWithoutTotal 计算混排位置对应的内容下标,不判断该位置是否为广告。
func ContentIndexAtPositionWithoutTotal(pos, adsTotal, interval int64) int64 {
return contentIndexAtMixedPosition(pos, adsTotal, interval)
}
// AdIndexAtPositionWithoutTotal 计算混排位置对应的广告下标,返回 -1 表示该位置不是广告。
func AdIndexAtPositionWithoutTotal(pos, adsTotal, interval int64) int64 {
if adsTotal <= 0 || interval <= 0 || pos < 0 {
return -1
}
cycle := interval + 1
if pos >= adsTotal*cycle {
return -1
}
if pos%cycle != interval {
return -1
}
return pos / cycle
}
// contentIndexAtMixedPosition 返回混排位置之前已经出现的内容数量。
func contentIndexAtMixedPosition(pos, adsTotal, interval int64) int64 {
if pos <= 0 {
return 0
}
if adsTotal <= 0 || interval <= 0 {
return pos
}
adCycle := interval + 1
adAreaSize := adsTotal * adCycle
if pos >= adAreaSize {
return pos - adsTotal
}
fullCycles := pos / adCycle
remain := pos % adCycle
return fullCycles*interval + minInt64(remain, interval)
}
func minInt64(a, b int64) int64 {
if a < b {
return a
}
return b
}
// AdIndexAtPosition 计算混排位置对应的广告下标,返回 -1 表示该位置不是广告。
func AdIndexAtPosition(pos, contentTotal, adsTotal, interval int64) int64 {
if interval <= 0 {
return -1
}
nextContent := FirstContentIndexAtOrAfter(pos+1, contentTotal, adsTotal, interval)
prevContent := nextContent - 1
if prevContent >= 0 && (prevContent+1)%interval == 0 && (prevContent+1)/interval-1 < adsTotal {
if ContentPosition(prevContent, adsTotal, interval)+1 == pos {
return (prevContent+1)/interval - 1
}
}
return -1
}