docs(plan): 复核残留修订——exportListToCsv真实签名落地代码块/调度测试改相对日期
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -2625,10 +2625,17 @@ async def sched_engine():
|
|||||||
|
|
||||||
|
|
||||||
async def test_analytics_tick_runs(sched_engine):
|
async def test_analytics_tick_runs(sched_engine):
|
||||||
|
# _analytics_tick 用真实「今天」算滚动窗——seed 的 day 必须相对当日,
|
||||||
|
# 固定 20260904 会在三天后过期(评审残留#2 修正)
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
from app.storage.summary import day_key
|
||||||
|
|
||||||
engine, maker = sched_engine
|
engine, maker = sched_engine
|
||||||
|
today = day_key(int(dt.datetime.now().timestamp() * 1000))
|
||||||
async with maker() as s:
|
async with maker() as s:
|
||||||
s.add(MsgDailyStats(channel="wechat", owner_account_id="wx1",
|
s.add(MsgDailyStats(channel="wechat", owner_account_id="wx1",
|
||||||
peer_kind="friend", peer_id="t1", day=20260904,
|
peer_kind="friend", peer_id="t1", day=today,
|
||||||
send_cnt=1, recv_cnt=0, last_ts=1756950000000,
|
send_cnt=1, recv_cnt=0, last_ts=1756950000000,
|
||||||
last_is_send=True))
|
last_is_send=True))
|
||||||
s.add(SensitiveWord(word="私单"))
|
s.add(SensitiveWord(word="私单"))
|
||||||
@@ -3061,7 +3068,7 @@ onMounted(load)
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { getChurn, type Channel, type ChurnRow } from '@/api/analytics'
|
import { getChurn, type Channel, type ChurnRow } from '@/api/analytics'
|
||||||
import { exportRowsCsv } from '@/utils/csvExport' // 路径以项目实际为准(grep csvExport)
|
import { exportListToCsv } from '@/utils/csvExport'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const channel = ref<Channel>('wechat')
|
const channel = ref<Channel>('wechat')
|
||||||
@@ -3105,20 +3112,26 @@ function goChat(row: ChurnRow) {
|
|||||||
async function exportCsv() {
|
async function exportCsv() {
|
||||||
exporting.value = true
|
exporting.value = true
|
||||||
try {
|
try {
|
||||||
const all: ChurnRow[] = []
|
await exportListToCsv<ChurnRow>({
|
||||||
for (let p = 1; ; p++) {
|
fetchPage: (page, pageSize) => getChurn({
|
||||||
const out = await getChurn({
|
|
||||||
channel: channel.value, type: kind.value, tier: tier.value,
|
channel: channel.value, type: kind.value, tier: tier.value,
|
||||||
min_msgs: 5, page: p, page_size: 100,
|
min_msgs: 5, page, page_size: pageSize,
|
||||||
})
|
}),
|
||||||
all.push(...out.items)
|
columns: [
|
||||||
if (all.length >= out.total || out.items.length === 0) break
|
{ key: 'nickname', label: '客户' },
|
||||||
}
|
{ key: 'channel', label: '渠道' },
|
||||||
exportRowsCsv(all.map((r) => ({
|
{ key: 'owner_account_id', label: '归属员工' },
|
||||||
客户: r.nickname, 渠道: r.channel, 归属员工: r.owner_account_id ?? '',
|
{
|
||||||
最后沟通: r.last_chat_time ? new Date(r.last_chat_time).toLocaleString('zh-CN', { hour12: false }) : '',
|
key: 'last_chat_time', label: '最后沟通',
|
||||||
历史消息量: r.hist_msgs, 活跃天数: r.active_days,
|
format: (r) => (r.last_chat_time
|
||||||
})), `流失预警_${kind.value}_${channel.value}.csv`)
|
? new Date(r.last_chat_time).toLocaleString('zh-CN', { hour12: false })
|
||||||
|
: '从未沟通'),
|
||||||
|
},
|
||||||
|
{ key: 'hist_msgs', label: '历史消息量' },
|
||||||
|
{ key: 'active_days', label: '活跃天数' },
|
||||||
|
],
|
||||||
|
filename: `流失预警_${kind.value}_${channel.value}`,
|
||||||
|
}) // 工具自带翻页+BOM+10000 行 cap
|
||||||
} finally {
|
} finally {
|
||||||
exporting.value = false
|
exporting.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user