- 后端新增 /admin/stats/report 接口,生成14天运营数据报告 - 报告内容:垃圾信息变化趋势、高频风险词Top10、误判率趋势 - 前端运营看板增加"生成报告"按钮,展示完整报告 - 支持复制报告文本到剪贴板 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
143 lines
4.2 KiB
JavaScript
143 lines
4.2 KiB
JavaScript
const { request } = require('../../utils/request')
|
|
|
|
Page({
|
|
data: {
|
|
loading: false,
|
|
stats: null,
|
|
kpis: [],
|
|
bars: [],
|
|
sourceDist: [],
|
|
topKeywords: [],
|
|
report: null,
|
|
reportLoading: false
|
|
},
|
|
|
|
formatPercent(value, digits = 2) {
|
|
const num = Number(value || 0)
|
|
return `${(num * 100).toFixed(digits)}%`
|
|
},
|
|
|
|
onShow() {
|
|
this.fetchStats()
|
|
},
|
|
|
|
onPullDownRefresh() {
|
|
this.fetchStats(true)
|
|
},
|
|
|
|
normalizeKpis(stats) {
|
|
return [
|
|
{ label: '系统用户', value: stats.user_count || 0 },
|
|
{ label: '发布总量', value: stats.post_count || 0 },
|
|
{ label: '拦截总量', value: stats.blocked_count || 0 },
|
|
{ label: '待处理申诉', value: stats.pending_appeal_count || 0 },
|
|
{ label: '训练样本', value: stats.sample_count || 0 },
|
|
{ label: '近7天拦截率', value: this.formatPercent(stats.blocked_ratio_7d, 2) }
|
|
]
|
|
},
|
|
|
|
normalizeBars(trend) {
|
|
const rows = Array.isArray(trend) ? trend : []
|
|
const maxVal = Math.max(1, ...rows.map((r) => Number(r.post_count || 0)))
|
|
return rows.map((row) => {
|
|
const value = Number(row.post_count || 0)
|
|
const ratio = value / maxVal
|
|
return {
|
|
...row,
|
|
value,
|
|
percent_text: `${Math.max(6, Math.round(ratio * 100))}%`
|
|
}
|
|
})
|
|
},
|
|
|
|
async fetchStats(fromPullDown = false) {
|
|
this.setData({ loading: true })
|
|
try {
|
|
const stats = await request({ url: '/admin/stats' })
|
|
const normalizedStats = {
|
|
...stats,
|
|
threshold_text: stats && stats.threshold ? this.formatPercent(stats.threshold.spam_threshold, 1) : '--'
|
|
}
|
|
this.setData({
|
|
stats: normalizedStats,
|
|
kpis: this.normalizeKpis(normalizedStats),
|
|
bars: this.normalizeBars(normalizedStats.trend_7d || []),
|
|
sourceDist: normalizedStats.source_distribution || [],
|
|
topKeywords: normalizedStats.top_keywords || []
|
|
})
|
|
} finally {
|
|
this.setData({ loading: false })
|
|
if (fromPullDown) wx.stopPullDownRefresh()
|
|
}
|
|
},
|
|
|
|
async generateReport() {
|
|
this.setData({ reportLoading: true })
|
|
try {
|
|
const report = await request({ url: '/admin/stats/report' })
|
|
|
|
// 处理趋势数据,计算进度条宽度
|
|
const spamTrend = (report.spam_trend || []).map((item) => {
|
|
const maxBlocked = Math.max(...(report.spam_trend || []).map((r) => r.blocked || 0), 1)
|
|
return {
|
|
...item,
|
|
blocked_percent: `${Math.max(4, Math.round((item.blocked || 0) / maxBlocked * 100))}%`
|
|
}
|
|
})
|
|
|
|
const misjudgeTrend = (report.misjudge_trend || []).map((item) => ({
|
|
...item,
|
|
rate_percent: `${Math.round((item.misjudge_rate || 0) * 100)}%`
|
|
}))
|
|
|
|
this.setData({
|
|
report: {
|
|
...report,
|
|
spam_trend: spamTrend,
|
|
misjudge_trend: misjudgeTrend
|
|
}
|
|
})
|
|
|
|
wx.showToast({ title: '报告已生成', icon: 'success' })
|
|
} finally {
|
|
this.setData({ reportLoading: false })
|
|
}
|
|
},
|
|
|
|
closeReport() {
|
|
this.setData({ report: null })
|
|
},
|
|
|
|
copyReportText() {
|
|
const report = this.data.report
|
|
if (!report) return
|
|
|
|
const summary = report.summary || {}
|
|
const lines = [
|
|
`【垃圾信息运营报告】`,
|
|
`报告周期:${report.period}`,
|
|
`生成时间:${(report.report_date || '').replace('T', ' ').slice(0, 19)}`,
|
|
'',
|
|
`【汇总统计】`,
|
|
`总发布量:${summary.total_posts || 0} 条`,
|
|
`拦截量:${summary.total_blocked || 0} 条`,
|
|
`正常发布:${summary.total_published || 0} 条`,
|
|
`拦截率:${this.formatPercent(summary.blocked_ratio, 2)}`,
|
|
`复核总数:${summary.total_reviews || 0} 次`,
|
|
`误判放行:${summary.total_approved || 0} 次`,
|
|
`平均误判率:${summary.avg_misjudge_rate_text || '0%'}`,
|
|
'',
|
|
`【高频风险词 Top10】`,
|
|
(report.top_keywords || []).slice(0, 10).map((k) => `${k.token}(${k.count}次)`).join('、'),
|
|
'',
|
|
`【近7日趋势】`,
|
|
(report.spam_trend || []).slice(-7).map((t) => `${t.label}: 拦截${t.blocked}条,发布${t.published}条`).join('\n')
|
|
]
|
|
|
|
wx.setClipboardData({
|
|
data: lines.join('\n'),
|
|
success: () => wx.showToast({ title: '报告已复制', icon: 'success' })
|
|
})
|
|
}
|
|
})
|