158 lines
4.6 KiB
JavaScript
158 lines
4.6 KiB
JavaScript
const { getRedisClient } = require("./client")
|
|
|
|
const SELLER_DEAL_INDEX_KEY_PREFIX = "index:seller:"
|
|
const SELLER_DEAL_INDEX_KEY_SUFFIX = ":deals"
|
|
|
|
function normalizePositiveInt(value) {
|
|
const num = Number(value)
|
|
if (!Number.isInteger(num) || num <= 0) return null
|
|
return num
|
|
}
|
|
|
|
function normalizeEpochMs(value) {
|
|
const num = Number(value)
|
|
if (!Number.isFinite(num) || num <= 0) return null
|
|
return Math.floor(num)
|
|
}
|
|
|
|
function toEpochMs(value) {
|
|
if (value instanceof Date) return value.getTime()
|
|
const date = new Date(value)
|
|
if (Number.isNaN(date.getTime())) return null
|
|
return date.getTime()
|
|
}
|
|
|
|
function isActiveStatus(status) {
|
|
return String(status || "").toUpperCase() === "ACTIVE"
|
|
}
|
|
|
|
function getSellerDealIndexKey(sellerId) {
|
|
const sid = normalizePositiveInt(sellerId)
|
|
if (!sid) return null
|
|
return `${SELLER_DEAL_INDEX_KEY_PREFIX}${sid}${SELLER_DEAL_INDEX_KEY_SUFFIX}`
|
|
}
|
|
|
|
function normalizeDealIndexPayload(payload = {}) {
|
|
const dealId = normalizePositiveInt(payload.dealId ?? payload.id)
|
|
const sellerId = normalizePositiveInt(payload.sellerId)
|
|
const createdAtTs =
|
|
normalizeEpochMs(payload.createdAtTs) ?? normalizeEpochMs(toEpochMs(payload.createdAt))
|
|
const status = String(payload.status || "").toUpperCase()
|
|
return { dealId, sellerId, createdAtTs, status }
|
|
}
|
|
|
|
function isIndexableDeal(payload = {}) {
|
|
const normalized = normalizeDealIndexPayload(payload)
|
|
return Boolean(
|
|
normalized.dealId &&
|
|
normalized.sellerId &&
|
|
normalized.createdAtTs &&
|
|
isActiveStatus(normalized.status)
|
|
)
|
|
}
|
|
|
|
function addDealToSellerIndexInPipeline(pipeline, payload = {}) {
|
|
const normalized = normalizeDealIndexPayload(payload)
|
|
if (!normalized.dealId || !normalized.sellerId || !normalized.createdAtTs) return false
|
|
if (!isActiveStatus(normalized.status)) return false
|
|
const key = getSellerDealIndexKey(normalized.sellerId)
|
|
if (!key) return false
|
|
pipeline.zadd(key, String(normalized.createdAtTs), String(normalized.dealId))
|
|
return true
|
|
}
|
|
|
|
function removeDealFromSellerIndexInPipeline(pipeline, payload = {}) {
|
|
const normalized = normalizeDealIndexPayload(payload)
|
|
if (!normalized.dealId || !normalized.sellerId) return false
|
|
const key = getSellerDealIndexKey(normalized.sellerId)
|
|
if (!key) return false
|
|
pipeline.zrem(key, String(normalized.dealId))
|
|
return true
|
|
}
|
|
|
|
async function reconcileDealSellerIndex({ before = null, after = null } = {}) {
|
|
const prev = normalizeDealIndexPayload(before || {})
|
|
const next = normalizeDealIndexPayload(after || {})
|
|
|
|
const prevIndexable = isIndexableDeal(prev)
|
|
const nextIndexable = isIndexableDeal(next)
|
|
|
|
const redis = getRedisClient()
|
|
const pipeline = redis.pipeline()
|
|
let commandCount = 0
|
|
|
|
if (prevIndexable) {
|
|
const removedForStatus = !nextIndexable
|
|
const movedSeller = nextIndexable && prev.sellerId !== next.sellerId
|
|
if (removedForStatus || movedSeller) {
|
|
if (removeDealFromSellerIndexInPipeline(pipeline, prev)) commandCount += 1
|
|
}
|
|
}
|
|
|
|
if (nextIndexable) {
|
|
const isNew = !prevIndexable
|
|
const movedSeller = prevIndexable && prev.sellerId !== next.sellerId
|
|
const scoreChanged =
|
|
prevIndexable &&
|
|
prev.sellerId === next.sellerId &&
|
|
Number(prev.createdAtTs) !== Number(next.createdAtTs)
|
|
if (isNew || movedSeller || scoreChanged) {
|
|
if (addDealToSellerIndexInPipeline(pipeline, next)) commandCount += 1
|
|
}
|
|
}
|
|
|
|
if (!commandCount) return 0
|
|
try {
|
|
await pipeline.exec()
|
|
return commandCount
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
async function getRecentDealIdsBySeller({
|
|
sellerId,
|
|
offset = 0,
|
|
limit = 30,
|
|
} = {}) {
|
|
const sid = normalizePositiveInt(sellerId)
|
|
if (!sid) return []
|
|
const key = getSellerDealIndexKey(sid)
|
|
if (!key) return []
|
|
const safeOffset = Math.max(0, Number(offset) || 0)
|
|
const safeLimit = Math.max(1, Math.min(Number(limit) || 30, 300))
|
|
const redis = getRedisClient()
|
|
|
|
try {
|
|
const ids = await redis.zrevrange(key, safeOffset, safeOffset + safeLimit - 1)
|
|
return Array.isArray(ids)
|
|
? ids.map((id) => Number(id)).filter((id) => Number.isInteger(id) && id > 0)
|
|
: []
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
async function getSellerDealIndexCount(sellerId) {
|
|
const sid = normalizePositiveInt(sellerId)
|
|
if (!sid) return 0
|
|
const key = getSellerDealIndexKey(sid)
|
|
if (!key) return 0
|
|
const redis = getRedisClient()
|
|
try {
|
|
const count = await redis.zcard(key)
|
|
return Number.isFinite(Number(count)) ? Number(count) : 0
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
getSellerDealIndexKey,
|
|
addDealToSellerIndexInPipeline,
|
|
removeDealFromSellerIndexInPipeline,
|
|
reconcileDealSellerIndex,
|
|
getRecentDealIdsBySeller,
|
|
getSellerDealIndexCount,
|
|
}
|