Add resumable US policy news collectors and provenance
Collect official crypto and US policy documents with deterministic screening, full bodies and attachments, bounded retries, and durable history cursors. Preserve publication precision and source identity while keeping new collectors disabled. Add real Congress, GovInfo, DOJ and Federal Register fixtures, offline capture replay, isolated database validation, and rollout instructions. Validate 982 backend tests, frontend checks, and 57 deployment control tests.
This commit is contained in:
1 parent
dc8a283571
commit
89c729092c
31 files changed
+2199
-23
No files matched your search
@@ -17,6 +17,9 @@ export interface SourceInfo {
|
||||
last_success_at: string | null
|
||||
last_error: string | null
|
||||
paused_until: string | null
|
||||
collection_stats?: Record<string, number>
|
||||
configuration_error?: string | null
|
||||
history_cursor?: { history?: { scanned?: boolean }, realtime?: { entry: number } }
|
||||
supports_history: boolean
|
||||
history_target: string | null
|
||||
history_position: string | null
|
||||
|
||||
@@ -8,6 +8,8 @@ export interface NewsBrief {
|
||||
author: string | null
|
||||
tags: string[]
|
||||
is_paywalled: boolean
|
||||
published_at_precision?: string | null
|
||||
published_date?: string | null
|
||||
published_at: string
|
||||
first_seen_at: string
|
||||
revision_no: number
|
||||
|
||||
@@ -54,8 +54,8 @@ async function saveInterval() {
|
||||
emit("changed")
|
||||
}
|
||||
|
||||
// 默认回补到 2026 年初;已设置过目标时沿用
|
||||
const DEFAULT_HISTORY_TARGET = "2026-01-01"
|
||||
// Default to 2025; retain a previously selected target.
|
||||
const DEFAULT_HISTORY_TARGET = "2025-01-01"
|
||||
const historyDate = ref(props.source.history_target ? dayjs(props.source.history_target).format("YYYY-MM-DD") : DEFAULT_HISTORY_TARGET)
|
||||
|
||||
const history = computed(() => {
|
||||
@@ -68,7 +68,7 @@ const history = computed(() => {
|
||||
const percent = Math.min(100, Math.max(0, Math.round(covered / span * 1000) / 10))
|
||||
const reached = formatTime(position, "YYYY-MM-DD HH:mm")
|
||||
if (done && position && new Date(position) <= new Date(target))
|
||||
return { text: `已完成,已到 ${reached}`, percent: 100, status: "success" } as const
|
||||
return { text: `列表遍历完成,已到 ${reached};正文及附件缺口以条目状态为准`, percent: 100, status: "success" } as const
|
||||
if (done)
|
||||
return { text: `来源最早只能翻到 ${reached},更早的数据接口不提供`, percent, status: "warning" } as const
|
||||
return { text: position ? `进行中,已到 ${reached}` : "等待下一轮开始", percent, status: undefined }
|
||||
@@ -88,6 +88,8 @@ async function cancelHistory() {
|
||||
}
|
||||
|
||||
const statusTag = computed(() => {
|
||||
if (props.source.configuration_error)
|
||||
return { type: "warning", text: props.source.configuration_error } as const
|
||||
const process = props.source.process
|
||||
if (props.restarting || process?.state === "restarting")
|
||||
return { type: "warning", text: "重启中" } as const
|
||||
@@ -121,7 +123,7 @@ onMounted(loadLatest)
|
||||
告警
|
||||
</el-tag>
|
||||
<div class="flex-1" />
|
||||
<el-switch :model-value="source.enabled" @change="toggleEnabled" />
|
||||
<el-switch :disabled="Boolean(source.configuration_error)" :model-value="source.enabled" @change="toggleEnabled" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="h-2 w-2 rounded-full" :style="{ background: `var(--el-color-${statusTag.type})` }" />
|
||||
@@ -155,6 +157,12 @@ onMounted(loadLatest)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="source.collection_stats?.unresolved_bills" class="text-xs text-amber-600">
|
||||
待核验法案 {{ source.collection_stats.unresolved_bills }};官方资料尚不充分
|
||||
</div>
|
||||
<div v-if="source.collection_stats?.discovered" class="text-xs text-slate-500">
|
||||
本轮发现 {{ source.collection_stats.discovered }};筛除 {{ source.collection_stats.excluded ?? 0 }};附件缺口 {{ source.collection_stats.partial ?? 0 }}
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-slate-500">
|
||||
<span>今日新增 <b class="num text-[var(--el-color-primary)]">+{{ source.today_new }}</b></span>
|
||||
<span>今日修订 <b class="num text-slate-900">{{ source.today_revised }}</b></span>
|
||||
|
||||
@@ -89,7 +89,7 @@ const diffStats = computed(() => ({
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-x-6 gap-y-2 border border-slate-200 rounded-lg border-solid bg-slate-50 px-3 py-2 text-sm">
|
||||
<div>
|
||||
<span class="label">发布时间</span><span class="num">{{ formatTime(detail.published_at, "YYYY-MM-DD HH:mm:ss") }}</span>
|
||||
<span class="label">发布时间</span><span class="num">{{ detail.published_at_precision === "date" ? `${detail.published_date}(仅日期)` : formatTime(detail.published_at, "YYYY-MM-DD HH:mm:ss") }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">作者</span>{{ detail.author || "-" }}
|
||||
@@ -112,6 +112,13 @@ const diffStats = computed(() => ({
|
||||
title="此条新闻仅采集公开摘要,完整内容请查看原文链接。"
|
||||
type="info" :closable="false" show-icon
|
||||
/>
|
||||
<el-alert v-if="detail.extra.content_scope === 'partial'" title="正文或关键附件存在采集缺口,请核对官方原文。" type="warning" :closable="false" />
|
||||
<div v-if="Array.isArray(detail.extra.attachments)" class="flex flex-wrap gap-2 text-xs">
|
||||
<span v-for="attachment in (detail.extra.attachments as { url: string, title: string, status: string }[])" :key="attachment.url">
|
||||
<el-link :href="attachment.url" target="_blank" type="primary">{{ attachment.title || '附件' }}</el-link>
|
||||
({{ attachment.status === 'complete' ? '已采集' : '存在缺口' }})
|
||||
</span>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab" class="min-h-0 flex flex-1 flex-col">
|
||||
<el-tab-pane :label="summaryOnly ? '摘要' : '正文'" name="content" class="h-full">
|
||||
<iframe :srcdoc="srcdoc" sandbox="allow-popups allow-popups-to-escape-sandbox" class="h-full w-full border-0" />
|
||||
|
||||
@@ -68,7 +68,7 @@ onBeforeUnmount(() => media?.removeEventListener("change", updateMobile))
|
||||
<el-tag size="small">
|
||||
{{ sourceNames[item.source_code] ?? item.source_code }}
|
||||
</el-tag>
|
||||
<span class="num">{{ formatTime(item.published_at) }}</span>
|
||||
<span class="num">{{ item.published_at_precision === "date" ? item.published_date : formatTime(item.published_at) }}</span>
|
||||
<div class="flex-1" />
|
||||
<el-tag v-if="item.revision_no > 1" size="small" type="warning">
|
||||
已修订 {{ item.revision_no - 1 }} 次
|
||||
|
||||
Reference in new issue
Block a user