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 }