feat: 优化复读榜

This commit is contained in:
digua
2025-11-30 01:48:56 +08:00
parent 9bf208d931
commit d10ecdf2ba
7 changed files with 154 additions and 46 deletions
+43 -2
View File
@@ -109,8 +109,28 @@ export function getRepeatAnalysis(sessionId: string, filter?: TimeFilter): any {
} else {
contentStats.set(content, { count: 1, maxChainLength: chainLength, originatorId, lastTs: chainTs })
}
// 计算反应时间 (Fastest Follower)
// 从第二个消息开始,计算与前一条消息的时间差
for (let i = 1; i < chain.length; i++) {
const currentMsg = chain[i]
const prevMsg = chain[i - 1]
const diff = (currentMsg.ts - prevMsg.ts) * 1000 // 毫秒
// 只统计 60 秒内的复读,排除间隔过久的“伪复读”
if (diff <= 60 * 1000) {
if (!fastestRepeaterStats.has(currentMsg.senderId)) {
fastestRepeaterStats.set(currentMsg.senderId, { totalDiff: 0, count: 0 })
}
const stats = fastestRepeaterStats.get(currentMsg.senderId)!
stats.totalDiff += diff
stats.count++
}
}
}
const fastestRepeaterStats = new Map<number, { totalDiff: number; count: number }>()
for (const msg of messages) {
if (!memberInfo.has(msg.senderId)) {
memberInfo.set(msg.senderId, { platformId: msg.platformId, name: msg.name })
@@ -171,6 +191,26 @@ export function getRepeatAnalysis(sessionId: string, filter?: TimeFilter): any {
return items.sort((a, b) => b.rate - a.rate)
}
const buildFastestList = (): any[] => {
const items: any[] = []
for (const [memberId, stats] of fastestRepeaterStats.entries()) {
// 过滤掉偶尔复读的人,至少参与5次复读才统计,避免数据偏差
if (stats.count < 5) continue
const info = memberInfo.get(memberId)
if (info) {
items.push({
memberId,
platformId: info.platformId,
name: info.name,
count: stats.count,
avgTimeDiff: Math.round(stats.totalDiff / stats.count),
})
}
}
return items.sort((a, b) => a.avgTimeDiff - b.avgTimeDiff) // 越快越好
}
const chainLengthDistribution: any[] = []
for (const [length, count] of chainLengthCount.entries()) {
chainLengthDistribution.push({ length, count })
@@ -189,17 +229,18 @@ export function getRepeatAnalysis(sessionId: string, filter?: TimeFilter): any {
})
}
hotContents.sort((a, b) => b.maxChainLength - a.maxChainLength)
const top10HotContents = hotContents.slice(0, 10)
const top50HotContents = hotContents.slice(0, 50)
return {
originators: buildRankList(originatorCount, totalRepeatChains),
initiators: buildRankList(initiatorCount, totalRepeatChains),
breakers: buildRankList(breakerCount, totalRepeatChains),
fastestRepeaters: buildFastestList(),
originatorRates: buildRateList(originatorCount),
initiatorRates: buildRateList(initiatorCount),
breakerRates: buildRateList(breakerCount),
chainLengthDistribution,
hotContents: top10HotContents,
hotContents: top50HotContents,
avgChainLength: totalRepeatChains > 0 ? Math.round((totalChainLength / totalRepeatChains) * 100) / 100 : 0,
totalRepeatChains,
}