Repair official collection and retire exhausted material tasks
This commit is contained in:
1 parent
d5d350b7ec
commit
d0c2beb9b3
23 files changed
+399
-35
No files matched your search
@@ -12,7 +12,7 @@ export interface CalendarItem {
|
||||
unit: string | null
|
||||
importance: number
|
||||
scheduled_at: string
|
||||
status: "scheduled" | "waiting" | "delayed" | "released" | "withdrawn" | "error" | "historical" | "unavailable" | "recorded"
|
||||
status: "retired" | "scheduled" | "waiting" | "delayed" | "released" | "withdrawn" | "error" | "historical" | "unavailable" | "recorded"
|
||||
actual: string | null
|
||||
previous: string | null
|
||||
forecast: string | null
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface SourceInfo {
|
||||
}
|
||||
|
||||
export interface CollectionRetry {
|
||||
retired_at?: string
|
||||
document: { url: string, canonical?: string }
|
||||
attempts: number
|
||||
next_at: number
|
||||
@@ -51,6 +52,8 @@ export interface SourcesInfo {
|
||||
}
|
||||
|
||||
export interface CalendarSourceInfo {
|
||||
enabled: boolean
|
||||
retired_count: number
|
||||
code: string
|
||||
name: string
|
||||
region_code: string
|
||||
|
||||
@@ -26,7 +26,7 @@ const detail = ref<CalendarItem | null>(null)
|
||||
const country = ref("")
|
||||
const impactLabels = { pending: "待公布", bullish: "利多", bearish: "利空", neutral: "影响较小", uncertain: "待判断" }
|
||||
const countries = computed(() => data.value?.countries ?? [])
|
||||
const labels = { scheduled: "待公布", waiting: "获取中", delayed: "取数延迟", released: "已公布", withdrawn: "已撤回", error: "采集异常", historical: "仅有日程", unavailable: "官方未公布", recorded: "已收录" }
|
||||
const labels = { retired: "已停止采集", scheduled: "待公布", waiting: "获取中", delayed: "取数延迟", released: "已公布", withdrawn: "已撤回", error: "采集异常", historical: "仅有日程", unavailable: "官方未公布", recorded: "已收录" }
|
||||
const formatted = (time: string) => dayjs(time).tz(zone).format("MM-DD HH:mm:ss")
|
||||
const groups = computed(() => {
|
||||
const result = new Map<string, CalendarItem[]>()
|
||||
|
||||
@@ -16,12 +16,13 @@ const date = (at: string | null) => at ? formatTime(at) : "尚无记录"
|
||||
日历
|
||||
</el-tag>
|
||||
<div class="flex-1" />
|
||||
<el-tag :type="!source.worker_alive ? 'danger' : source.errors.length ? 'warning' : source.schedule_success_at ? 'success' : 'info'" size="small">
|
||||
{{ !source.worker_alive ? "进程离线" : source.errors.length ? "采集异常" : source.schedule_success_at ? "同步正常" : "待同步" }}
|
||||
<el-tag :type="!source.enabled ? 'info' : !source.worker_alive ? 'danger' : source.errors.length ? 'warning' : source.schedule_success_at ? 'success' : 'info'" size="small">
|
||||
{{ !source.enabled ? "已停用" : !source.worker_alive ? "进程离线" : source.errors.length ? "采集异常" : source.schedule_success_at ? "同步正常" : "待同步" }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-2 text-xs text-slate-500">
|
||||
<dt>已停止采集</dt><dd>{{ source.retired_count }} 场(历史资料保留)</dd>
|
||||
<dt>进程心跳</dt><dd>{{ date(source.heartbeat_at) }}</dd>
|
||||
<dt>日程最近检查</dt><dd>{{ date(source.schedule_checked_at) }}</dd>
|
||||
<dt>日程最近成功</dt><dd>{{ date(source.schedule_success_at) }}</dd>
|
||||
|
||||
@@ -144,21 +144,24 @@ onMounted(loadLatest)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="source.collection_stats?.unresolved_bills" class="text-xs text-amber-600">
|
||||
<div v-if="source.collection_stats?.active_gaps !== 0 && source.collection_stats?.unresolved_bills" class="text-xs text-amber-600">
|
||||
待核验法案 {{ source.collection_stats.unresolved_bills }};待重试 {{ source.collection_stats.bill_retry_pending }};重试已耗尽 {{ source.collection_stats.bill_retry_exhausted }}
|
||||
</div>
|
||||
<div v-if="source.collection_stats?.attachment_gaps" class="text-xs text-amber-600">
|
||||
<div v-if="source.collection_stats?.active_gaps !== 0 && source.collection_stats?.attachment_gaps" class="text-xs text-amber-600">
|
||||
附件待核验文档 {{ source.collection_stats.attachment_gaps }};待重试 {{ source.collection_stats.attachment_retry_pending }};重试已耗尽 {{ source.collection_stats.attachment_retry_exhausted }},请核对原文
|
||||
</div>
|
||||
<div v-if="source.collection_stats?.document_gaps" class="text-xs text-amber-600">
|
||||
<div v-if="source.collection_stats?.active_gaps !== 0 && source.collection_stats?.document_gaps" class="text-xs text-amber-600">
|
||||
正文待核验 {{ source.collection_stats.document_gaps }};待重试 {{ source.collection_stats.document_retry_pending }};重试已耗尽 {{ source.collection_stats.document_retry_exhausted }},请核对来源日志
|
||||
</div>
|
||||
<p v-if="source.collection_stats?.retired_count" class="text-xs text-slate-500">
|
||||
已停止采集 {{ source.collection_stats.retired_count }} 条,原因及原文保留在详情中。
|
||||
</p>
|
||||
<details v-if="gaps.length" class="text-xs text-amber-700">
|
||||
<summary class="cursor-pointer">
|
||||
缺口详情({{ gaps.length }})
|
||||
</summary>
|
||||
<div v-for="gap in gaps" :key="gap.key" class="mt-2 border border-amber-200 rounded border-solid p-2">
|
||||
<div>{{ gap.label }};已尝试 {{ gap.attempts }} 次;{{ gap.exhausted ? "重试已耗尽" : `下次重试不早于 ${formatTime(gap.nextAt)}` }}</div>
|
||||
<div>{{ gap.label }};已尝试 {{ gap.attempts }} 次;{{ gap.retired ? "已停止采集" : gap.exhausted ? "重试已耗尽" : `下次重试不早于 ${formatTime(gap.nextAt)}` }}</div>
|
||||
<div class="my-1 whitespace-pre-wrap break-words">
|
||||
{{ gap.error }}
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ export function sourceStatus(source: SourceInfo, restarting = false, now = Date.
|
||||
return { type: "primary", text: "采集中" } as const
|
||||
if (source.consecutive_failures)
|
||||
return { type: "danger", text: "失败待重试" } as const
|
||||
if (source.collection_stats?.document_gaps || source.collection_stats?.attachment_gaps || source.collection_stats?.unresolved_bills)
|
||||
if ((source.collection_stats?.active_gaps ?? (source.collection_stats?.document_gaps || source.collection_stats?.attachment_gaps || source.collection_stats?.unresolved_bills)))
|
||||
return { type: "warning", text: "存在采集缺口" } as const
|
||||
return { type: "info", text: "等待调度" } as const
|
||||
}
|
||||
@@ -49,6 +49,7 @@ export function collectionGaps(source: Pick<SourceInfo, "history_cursor">) {
|
||||
url: originalUrl(retry.document.canonical ?? retry.document.url),
|
||||
error: retry.error || "历史记录未保存具体原因,请核对原文",
|
||||
attempts: retry.attempts,
|
||||
retired: !!retry.retired_at,
|
||||
exhausted: retry.attempts >= 3,
|
||||
nextAt: new Date(retry.next_at * 1000).toISOString()
|
||||
})))
|
||||
|
||||
@@ -102,3 +102,32 @@ test("gap links preserve canonical pages and do not rewrite unrelated URLs", ()
|
||||
assert.equal(collectionGaps(source)[0].url, url)
|
||||
}
|
||||
})
|
||||
|
||||
test("retired gaps retain evidence without an active warning", () => {
|
||||
const source = {
|
||||
enabled: true,
|
||||
consecutive_failures: 0,
|
||||
code: "doj",
|
||||
name: "DOJ",
|
||||
region_code: "US",
|
||||
list_url: "https://www.justice.gov/",
|
||||
interval_seconds: 60,
|
||||
backfill_hours: 72,
|
||||
last_success_at: null,
|
||||
last_error: null,
|
||||
paused_until: null,
|
||||
supports_history: true,
|
||||
history_target: null,
|
||||
history_position: null,
|
||||
history_done: false,
|
||||
today_new: 0,
|
||||
today_revised: 0,
|
||||
total_items: 0,
|
||||
process: { state: "running", pid: 1, started_at: null, next_run_at: "2026-11-05T00:00:00Z" },
|
||||
collection_stats: { active_gaps: 0, document_gaps: 1, retired_count: 1 },
|
||||
history_cursor: { document_retries: { old: { document: { url: "https://www.justice.gov/old" }, attempts: 3, next_at: 1, error: "HTTP 503", retired_at: "2026-11-05T00:00:00Z" } } }
|
||||
} as SourceInfo
|
||||
assert.equal(sourceStatus(source).text, "等待调度")
|
||||
assert.equal(collectionGaps(source)[0].retired, true)
|
||||
assert.equal(collectionGaps(source)[0].error, "HTTP 503")
|
||||
})
|
||||
Reference in new issue
Block a user