feat(HEL-529): 按定稿100%重做三页数据中枢 + 数据修正1-5
视觉:admin/app.js 按已确认打样 hub-kimi.html 逐行重写三页 DOM 与动效
(sparkline/缓存年龄秒增/延迟变化闪烁/EVENT TAPE 预装滚动/分组接口表/
更新频率列/时钟冒号 blink/雷达 blip),CSS 补真实调用脉冲 node-ping。
数据修正:
1) pipeline._stage 批内按暂存表业务键确定性去重(保留最后一条),
修复人气榜/龙虎榜自 09-07 起每日 UNIQUE constraint 落库失败;
2) overview.anomalies 收敛为「最新批次未成功且当日未发布」的当前异常,
历史已恢复批次留在审计明细;
3) source_catalog 接口补真实批次分组 + 观测 join(接口名或数据集名
双向匹配,标注 observed/observed_basis),消除「全部未配置/0/30」误报;
4) lineage 逐数据集按其服务接口过滤健康行(接口级状态),
provisional 已配置无观测显示「已配置 · 待观测」,仅 iFinD 为未配置;
5) lineage 补 update_freq 真实频率字段。
测试:新增 tests/test_hel529_rework.py(8 项),全套 191 项通过
(1 项环境依赖失败在基线 d9358ab 上同样复现,与本改动无关)。
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -31,21 +31,21 @@ CATALOG: list[dict[str, Any]] = [
|
||||
"credential_key": "tushare_token",
|
||||
"status_source": "src_health (legacy, kept) + provider_health (unified, HEL-543)",
|
||||
"interfaces": [
|
||||
{"interface": "trade_cal", "capability": "交易日历", "datasets": ["calendar"]},
|
||||
{"interface": "stock_basic", "capability": "股票主档", "datasets": ["stocks"]},
|
||||
{"interface": "daily", "capability": "个股日K", "datasets": ["daily"]},
|
||||
{"interface": "adj_factor", "capability": "复权因子", "datasets": ["daily"]},
|
||||
{"interface": "daily_basic", "capability": "估值", "datasets": ["valuation"]},
|
||||
{"interface": "index_daily", "capability": "指数日K", "datasets": ["index_daily"]},
|
||||
{"interface": "moneyflow", "capability": "资金流", "datasets": ["moneyflow"]},
|
||||
{"interface": "stk_auction", "capability": "集合竞价", "datasets": ["auction"]},
|
||||
{"interface": "limit_list_d", "capability": "涨跌停池", "datasets": ["limit_events"]},
|
||||
{"interface": "ths_hot", "capability": "同花顺人气榜", "datasets": ["popularity"]},
|
||||
{"interface": "dc_hot", "capability": "东方财富人气榜", "datasets": ["popularity"]},
|
||||
{"interface": "hm_detail", "capability": "龙虎榜游资明细", "datasets": ["dragon_tiger"]},
|
||||
{"interface": "ths_daily", "capability": "同花顺概念行情", "datasets": ["sector_daily"]},
|
||||
{"interface": "dc_index", "capability": "东方财富板块行情", "datasets": ["sector_daily"]},
|
||||
{"interface": "sw_daily", "capability": "申万行业行情", "datasets": ["sector_daily"]},
|
||||
{"interface": "trade_cal", "capability": "交易日历", "datasets": ["calendar"], "group": "日历 / 主档"},
|
||||
{"interface": "stock_basic", "capability": "股票主档", "datasets": ["stocks"], "group": "日历 / 主档"},
|
||||
{"interface": "daily", "capability": "个股日K", "datasets": ["daily"], "group": "盘后 A 批"},
|
||||
{"interface": "adj_factor", "capability": "复权因子", "datasets": ["daily"], "group": "盘后 A 批"},
|
||||
{"interface": "daily_basic", "capability": "估值", "datasets": ["valuation"], "group": "盘后 A 批"},
|
||||
{"interface": "index_daily", "capability": "指数日K", "datasets": ["index_daily"], "group": "指数 B 批"},
|
||||
{"interface": "moneyflow", "capability": "资金流", "datasets": ["moneyflow"], "group": "盘后 A 批"},
|
||||
{"interface": "stk_auction", "capability": "集合竞价", "datasets": ["auction"], "group": "盘后 A 批"},
|
||||
{"interface": "limit_list_d", "capability": "涨跌停池", "datasets": ["limit_events"], "group": "扩展软批"},
|
||||
{"interface": "ths_hot", "capability": "同花顺人气榜", "datasets": ["popularity"], "group": "扩展软批"},
|
||||
{"interface": "dc_hot", "capability": "东方财富人气榜", "datasets": ["popularity"], "group": "扩展软批"},
|
||||
{"interface": "hm_detail", "capability": "龙虎榜游资明细", "datasets": ["dragon_tiger"], "group": "扩展软批"},
|
||||
{"interface": "ths_daily", "capability": "同花顺概念行情", "datasets": ["sector_daily"], "group": "扩展软批"},
|
||||
{"interface": "dc_index", "capability": "东方财富板块行情", "datasets": ["sector_daily"], "group": "扩展软批"},
|
||||
{"interface": "sw_daily", "capability": "申万行业行情", "datasets": ["sector_daily"], "group": "扩展软批"},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -55,13 +55,13 @@ CATALOG: list[dict[str, Any]] = [
|
||||
"credential_key": None,
|
||||
"status_source": "provider_health (unified, HEL-543)",
|
||||
"interfaces": [
|
||||
{"interface": "indices", "capability": "指数实时报价", "datasets": ["index_quotes"]},
|
||||
{"interface": "market_quotes", "capability": "全市场实时快照", "datasets": ["quotes_latest"]},
|
||||
{"interface": "named_quotes", "capability": "指定个股实时报价", "datasets": ["quotes_latest"]},
|
||||
{"interface": "sector_quote", "capability": "申万板块实时报价(单个)", "datasets": ["sectors_quote"]},
|
||||
{"interface": "sector_quotes_batch", "capability": "申万板块批量报价(预热)", "datasets": ["sectors_quote"]},
|
||||
{"interface": "limit_pool", "capability": "涨停/炸板池(盘中)", "datasets": ["limit_pool"]},
|
||||
{"interface": "intraday", "capability": "分时走势", "datasets": ["intraday_points"]},
|
||||
{"interface": "indices", "capability": "指数实时报价", "datasets": ["index_quotes"], "group": "实时快照"},
|
||||
{"interface": "market_quotes", "capability": "全市场实时快照", "datasets": ["quotes_latest"], "group": "实时快照"},
|
||||
{"interface": "named_quotes", "capability": "指定个股实时报价", "datasets": ["quotes_latest"], "group": "实时快照"},
|
||||
{"interface": "sector_quote", "capability": "申万板块实时报价(单个)", "datasets": ["sectors_quote"], "group": "实时快照"},
|
||||
{"interface": "sector_quotes_batch", "capability": "申万板块批量报价(预热)", "datasets": ["sectors_quote"], "group": "实时快照"},
|
||||
{"interface": "limit_pool", "capability": "涨停/炸板池(盘中)", "datasets": ["limit_pool"], "group": "实时快照"},
|
||||
{"interface": "intraday", "capability": "分时走势", "datasets": ["intraday_points"], "group": "实时快照"},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -71,13 +71,14 @@ CATALOG: list[dict[str, Any]] = [
|
||||
"credential_key": None,
|
||||
"status_source": "provider_health (unified, HEL-543)",
|
||||
"interfaces": [
|
||||
{"interface": "indices", "capability": "指数实时报价(东财失败时备用)", "datasets": ["index_quotes"]},
|
||||
{"interface": "indices", "capability": "指数实时报价(东财失败时备用)", "datasets": ["index_quotes"], "group": "实时备援"},
|
||||
{
|
||||
"interface": "market_quotes_fallback",
|
||||
"capability": "全市场快照(备用;按本地股票主档逐只请求拼接)",
|
||||
"datasets": ["quotes_latest"],
|
||||
"group": "实时备援",
|
||||
},
|
||||
{"interface": "named_quotes", "capability": "指定个股实时报价(东财失败时备用)", "datasets": ["quotes_latest"]},
|
||||
{"interface": "named_quotes", "capability": "指定个股实时报价(东财失败时备用)", "datasets": ["quotes_latest"], "group": "实时备援"},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -87,11 +88,11 @@ CATALOG: list[dict[str, Any]] = [
|
||||
"credential_key": "ifind_refresh_token",
|
||||
"status_source": "provider_health (unified, HEL-543) + adapter.status()",
|
||||
"interfaces": [
|
||||
{"interface": "wencai", "capability": "问财自然语言选股", "datasets": ["ifind_wencai"]},
|
||||
{"interface": "snapshots", "capability": "快照", "datasets": ["ifind_snapshots"]},
|
||||
{"interface": "history", "capability": "历史行情", "datasets": ["ifind_history"]},
|
||||
{"interface": "realtime", "capability": "实时行情", "datasets": ["ifind_realtime"]},
|
||||
{"interface": "intraday", "capability": "分时(高频)", "datasets": ["ifind_intraday"]},
|
||||
{"interface": "wencai", "capability": "问财自然语言选股", "datasets": ["ifind_wencai"], "group": "预留接口"},
|
||||
{"interface": "snapshots", "capability": "快照", "datasets": ["ifind_snapshots"], "group": "预留接口"},
|
||||
{"interface": "history", "capability": "历史行情", "datasets": ["ifind_history"], "group": "预留接口"},
|
||||
{"interface": "realtime", "capability": "实时行情", "datasets": ["ifind_realtime"], "group": "预留接口"},
|
||||
{"interface": "intraday", "capability": "分时(高频)", "datasets": ["ifind_intraday"], "group": "预留接口"},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -161,5 +162,35 @@ def snapshot(db: Any, auth: Any = None) -> list[dict[str, Any]]:
|
||||
except Exception:
|
||||
health_rows = []
|
||||
item["live_interfaces"] = health_rows
|
||||
# HEL-529 fix 3: 已登记 ≠ 已观测 ≠ 健康。观测记录有两种真实写法:
|
||||
# eastmoney/tencent 的 observe() 直接写接口名;tushare 官方管线
|
||||
# _log_call() 写的是数据集名(如 daily_basic 接口对应的数据集
|
||||
# valuation)。这里按「接口名 或 该接口声明的 datasets 之一」双向
|
||||
# 匹配,并为每个接口标注观测依据,禁止把"暂无观测"显示成"未配置"。
|
||||
by_interface = {str(row["interface"]): row for row in health_rows}
|
||||
for iface in item["interfaces"]:
|
||||
row = by_interface.get(str(iface["interface"]))
|
||||
basis = "interface" if row is not None else ""
|
||||
if row is None:
|
||||
for dataset in iface.get("datasets", []):
|
||||
candidate = by_interface.get(str(dataset))
|
||||
if candidate is not None:
|
||||
row = candidate
|
||||
basis = "dataset"
|
||||
break
|
||||
if row is None:
|
||||
iface["observed"] = False
|
||||
iface["observed_basis"] = ""
|
||||
iface["observed_state"] = ""
|
||||
iface["observed_at"] = ""
|
||||
iface["observed_latency_ms"] = None
|
||||
iface["observed_note"] = ""
|
||||
else:
|
||||
iface["observed"] = True
|
||||
iface["observed_basis"] = basis
|
||||
iface["observed_state"] = str(row["state"] or "")
|
||||
iface["observed_at"] = str(row["last_ok_at"] or row["updated_at"] or "")
|
||||
iface["observed_latency_ms"] = row["last_latency_ms"]
|
||||
iface["observed_note"] = str(row["last_error"] or row["last_fallback_reason"] or "")
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user