背景
- HEAD 长期停在 08-03 的安全审计提交,此后六周的工作全部只在工作区。
- 更严重的是部分源码从未进入过版本控制(非"改了没提交"):
· Services/findatahub-core/ 索引 0 条 —— 18 个核心模块(pipeline/gate/cleaner/
db/cross_validator/health/retry…)全新 clone 拿不到
· Services/findatahub-client/ 索引 0 条
· App/Sources 磁盘 74 个 .swift、索引 54 —— 20 个未入库(AutoRecoveryManager、
全套 Settings*Section、PowerSaver、MySQLOnDemandManager、DependencyScanner…)
一次 `git clean -fd`、误删目录或磁盘故障即可不可恢复地丢失这些文件。
本提交内容
1. 补齐上述未跟踪源码、测试、文档与报告(Services/App/Docs/Tests/根报告)。
2. 纳入 .workbuddy/memory(项目知识)与 .github CI 配置。
3. 本次代码与架构复查的修复(详见下一段说明):
- Scripts/bin/common.py PidManager.status 改 split(":", 1),修复时间戳被截断到整点
- Scripts/bin/db_pool.py 取连接加超时、归还前 rollback、失败归还池位、双检锁初始化
- Scripts/bin/newsflash_backend.py _query_all 改 try/finally 无条件归还连接
- Scripts/bin/newsflash_collect.py 新增 _install_crash_handlers(faulthandler + excepthook)
- tushare_fetcher/core/fetcher.py _fetch_static 增量走 upsert,仅全量快照才 replace
- tushare_fetcher/scripts/tushare_sync.sh 银层迁移补 stock_weekly/stock_monthly;
月度增加分红按报告期全量刷新
- kuaixun-service/extend/content_enricher.py 禁用自动重定向 + 逐跳 SSRF 校验
- App/.../AutoRecoveryManager.swift 反复故障改告警不熔断(避免拉长数据空窗)
- 新增 Scripts/bin/repair_dividend_history.py(分红历史修复,按报告期分页幂等 upsert)
- 新增 Docs/repro/*(两个可运行的缺陷复现脚本)
- 补齐 6 个文件共 20 条回归用例(均做过证伪:旧实现下必然失败)
安全
- 已确认未纳入任何凭据:Services/tushare_fetcher/.env 被其 .gitignore 覆盖;
~/.config/findatahub/env(0600)在仓库外。
验证
- Python 测试 1074 passed(Scripts/bin 207 / core 105 / tushare 174 / kuaixun 405 / akshare 183)
- Swift 经 `swift build --disable-sandbox` 编译通过,产物 v1.5.2 已部署
260 lines
17 KiB
HTML
260 lines
17 KiB
HTML
<!-- Generated by Trae Work -->
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>FinDataHub 项目实施分析报告</title>
|
||
<style>
|
||
:root{--bg:#f8fafc;--bg2:#fff;--ink:#1e293b;--muted:#64748b;--rule:#e2e8f0;--accent:#2563eb;--accent2:#7c3aed;--code-bg:#f1f5f9;--success:#059669;--warning:#d97706}
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
html{font-size:16px;scroll-behavior:smooth}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;font-size:15px;line-height:1.75;color:var(--ink);background:var(--bg)}
|
||
.container{max-width:1000px;margin:0 auto;padding:0 2rem}
|
||
@media(max-width:600px){.container{padding:0 1rem}}
|
||
|
||
.cover{text-align:center;padding:4rem 2rem 2.5rem;background:linear-gradient(135deg,var(--bg2)0%,var(--bg)100%);border-bottom:1px solid var(--rule)}
|
||
.cover .badge{display:inline-block;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--accent);background:var(--accent)11;padding:.3rem 1rem;border-radius:99px;margin-bottom:1.5rem}
|
||
.cover h1{font-size:2.2rem;font-weight:700;line-height:1.3;margin-bottom:.5rem}
|
||
.cover .sub{font-size:1rem;color:var(--muted);max-width:600px;margin:0 auto}
|
||
.cover .meta{font-size:.85rem;color:var(--muted);margin-top:1rem}
|
||
main{padding:2rem 0 4rem}
|
||
|
||
.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1rem;margin:1.5rem 0}
|
||
.metric{background:var(--bg2);border:1px solid var(--rule);border-radius:10px;padding:1.25rem;text-align:center}
|
||
.metric .num{font-size:2rem;font-weight:700;color:var(--accent);line-height:1.2}
|
||
.metric .lbl{font-size:.8rem;color:var(--muted);margin-top:.25rem}
|
||
|
||
h2{font-size:1.5rem;font-weight:700;margin-top:3rem;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:2px solid var(--rule)}
|
||
h3{font-size:1.15rem;font-weight:700;margin-top:2rem;margin-bottom:.75rem}
|
||
|
||
.table-wrap{overflow-x:auto;margin:1rem 0}
|
||
table{width:100%;border-collapse:collapse;font-size:.9em;border:1px solid var(--rule)}
|
||
thead th{background:var(--code-bg);font-weight:700;text-align:left;padding:.5rem .8rem;border-bottom:2px solid var(--rule);font-size:.8em;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
|
||
tbody td{padding:.45rem .8rem;border-bottom:1px solid var(--rule);vertical-align:top}
|
||
tbody tr:hover{background:var(--accent)04}
|
||
|
||
.callout{padding:.6rem 1rem;border-radius:6px;margin:1rem 0;border-left:4px solid var(--accent);background:var(--accent)08;font-size:.9em}
|
||
.callout.success{border-left-color:var(--success);background:var(--success)08}
|
||
|
||
.tag{display:inline-block;font-size:.65rem;font-weight:700;padding:.1rem .4rem;border-radius:3px;text-transform:uppercase;letter-spacing:.05em}
|
||
.tag-ok{background:var(--success)18;color:var(--success)}
|
||
.tag-wip{background:var(--warning)18;color:var(--warning)}
|
||
|
||
.chart-figure{margin:2rem 0}
|
||
.chart-figure figcaption{font-size:.85rem;font-weight:600;color:var(--ink);margin-bottom:.75rem}
|
||
.chart-row{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}
|
||
@media(max-width:700px){.chart-row{grid-template-columns:1fr}}
|
||
|
||
.card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1rem;margin:1rem 0}
|
||
.card{background:var(--bg2);border:1px solid var(--rule);border-radius:10px;padding:1.25rem}
|
||
.card h4{margin-top:0}
|
||
.card p{font-size:.9em;color:var(--muted);margin:0}
|
||
|
||
footer{padding:3rem 2rem;border-top:1px solid var(--rule);text-align:center}
|
||
footer p{font-size:.85rem;color:var(--muted)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="cover">
|
||
<div class="badge">Implementation Report</div>
|
||
<h1>FinDataHub 项目实施分析报告</h1>
|
||
<p class="sub">从数据采集到数据消费 — 三层架构全链路实施状态</p>
|
||
<p class="meta">2026-08-09 · v1.0 · 全部 P1-P5 阶段</p>
|
||
</header>
|
||
|
||
<main class="container">
|
||
|
||
<!-- ═══════════ 总览 ═══════════ -->
|
||
<h2>一、实施总览</h2>
|
||
|
||
<div class="metrics">
|
||
<div class="metric"><div class="num">14.7M</div><div class="lbl">数据总行数</div></div>
|
||
<div class="metric"><div class="num">5,751</div><div class="lbl">股票覆盖</div></div>
|
||
<div class="metric"><div class="num">2</div><div class="lbl">数据源</div></div>
|
||
<div class="metric"><div class="num">0.85</div><div class="lbl">质量评分</div></div>
|
||
<div class="metric"><div class="num">6</div><div class="lbl">微服务</div></div>
|
||
<div class="metric"><div class="num">100%</div><div class="lbl">P1-P4 完成</div></div>
|
||
</div>
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>阶段</th><th>内容</th><th>状态</th><th>关键指标</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><strong>P1</strong></td><td>采集层:双源交叉验证 + AKShare 修复 + 北交所重拉</td><td><span class="tag tag-ok">DONE</span></td><td>匹配率 96-100%</td></tr>
|
||
<tr><td><strong>P2</strong></td><td>预处理层:7 步 Pipeline + 4 级门控 + 策略画像</td><td><span class="tag tag-ok">DONE</span></td><td>质量分 0.85</td></tr>
|
||
<tr><td><strong>P3</strong></td><td>使用层:SDK 11 方法 + API 14 端点</td><td><span class="tag tag-ok">DONE</span></td><td>4 层递进接入</td></tr>
|
||
<tr><td><strong>P4</strong></td><td>AI 接入:Skills + MCP 7 工具</td><td><span class="tag tag-ok">DONE</span></td><td>自然语言直达数据</td></tr>
|
||
<tr><td><strong>P5</strong></td><td>云端扩展:阿里云 MySQL 同步 + 版本管理 + 血缘追踪</td><td><span class="tag tag-ok">DONE</span></td><td>14.8GB 全量同步</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ═══════════ 数据规模 ═══════════ -->
|
||
<h2>二、数据规模与质量</h2>
|
||
|
||
<div class="card-grid">
|
||
<div class="card"><h4>stock_daily</h4><p>12,670,167 行 · 5,751 只股票 · 2021-01-04 ~ 2026-08-07</p></div>
|
||
<div class="card"><h4>stock_weekly</h4><p>1,681,905 行 · 质量分 0.85</p></div>
|
||
<div class="card"><h4>stock_monthly</h4><p>395,487 行 · 质量分 0.85</p></div>
|
||
<div class="card"><h4>fina_indicator</h4><p>244,601 行 · 最新 ROE=2.65%</p></div>
|
||
<div class="card"><h4>stock_basic</h4><p>5,549 行 · 全市场股票基础信息</p></div>
|
||
<div class="card"><h4>辅助表(6 张)</h4><p>_data_issues / _table_meta / _retry_queue 等</p></div>
|
||
</div>
|
||
|
||
<figure class="chart-figure"><figcaption>图 1:本地数据规模(行数)</figcaption><div id="chart-scale" style="width:100%;min-height:300px"></div></figure>
|
||
|
||
<div class="chart-row">
|
||
<figure class="chart-figure"><figcaption>图 2:各板块股票分布</figcaption><div id="chart-market" style="width:100%;min-height:320px"></div></figure>
|
||
<figure class="chart-figure"><figcaption>图 3:阿里云 RDS 数据分布 (MB)</figcaption><div id="chart-rds" style="width:100%;min-height:320px"></div></figure>
|
||
</div>
|
||
|
||
<!-- ═══════════ 交叉验证 ═══════════ -->
|
||
<h2>三、双源交叉验证</h2>
|
||
|
||
<figure class="chart-figure"><figcaption>图 4:TuShare vs AKShare 匹配率历史趋势</figcaption><div id="chart-cv" style="width:100%;min-height:300px"></div></figure>
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>日期</th><th>对比对数</th><th>匹配率</th><th>差异</th><th>差异字段分布</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>2026-08-07</td><td>5,535</td><td>96.1%</td><td>216</td><td>pct_chg(217) / pre_close(101)</td></tr>
|
||
<tr><td>2026-07-01</td><td>5,511</td><td>99.8%</td><td>13</td><td>pct_chg(12) / pre_close(1)</td></tr>
|
||
<tr><td>2025-01-02</td><td>5,331</td><td>99.9%</td><td>3</td><td>pct_chg(2) / vol(1)</td></tr>
|
||
<tr><td>2024-01-02</td><td>5,241</td><td>99.9%</td><td>3</td><td>pre_close(3)</td></tr>
|
||
<tr><td>2023-01-03</td><td>4,927</td><td>96.7%</td><td>161</td><td>pct_chg(155) / pre_close(6)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<p>差异主要集中在<strong>涨跌幅(pct_chg)和昨收价(pre_close)</strong>两个字段,成交量/成交额差异极小(共 6 条)。最新日(2026-08-07)匹配率偏低至 96.1%,可能是某数据源更新时序延迟导致。</p>
|
||
|
||
<!-- ═══════════ 数据质量 ═══════════ -->
|
||
<h2>四、数据质量校验</h2>
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>检查项</th><th>结果</th><th>实现</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>OHLC 关系违规</td><td><strong>0 行</strong></td><td><code>cleaner.py</code> — 自动修正 high<low / open>high / close>high</td></tr>
|
||
<tr><td>价格为负</td><td><strong>0 行</strong></td><td><code>gate.py</code> — Gate3 BLOCK 拦截</td></tr>
|
||
<tr><td>AKShare 成交额异常</td><td>637 万行修正</td><td><code>cleaner._fix_akshare_broken_amount</code></td></tr>
|
||
<tr><td>科创板 vol 单位</td><td>688/689 ÷100</td><td><code>cleaner._fix_akshare_star_vol</code></td></tr>
|
||
<tr><td>AKShare 涨跌幅重算</td><td>从 close/pre_close 推导</td><td><code>cleaner._recompute_akshare_pct_chg</code></td></tr>
|
||
<tr><td>AKShare pre_close 时效</td><td>253,513 行更新</td><td><code>fix_akshare_preclose.py</code></td></tr>
|
||
<tr><td>北交所数据补齐</td><td>333 只 / 186,460 行</td><td><code>em_fetch.py</code> — 新浪源直连</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ═══════════ 模块清单 ═══════════ -->
|
||
<h2>五、完整模块清单</h2>
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>层级</th><th>模块</th><th>文件</th><th>行数</th><th>职责</th></tr></thead>
|
||
<tbody>
|
||
<tr><td rowspan="4"><strong>L1 采集</strong></td><td>tushare_fetcher</td><td><code>Services/tushare_fetcher/</code></td><td>~800</td><td>TuShare Pro API 封装 + 幂等写入</td></tr>
|
||
<tr><td>akshare_fetcher</td><td><code>Services/akshare_fetcher/</code></td><td>~1200</td><td>AKShare 腾讯/东财/新浪多接口路由</td></tr>
|
||
<tr><td>DatabaseManager</td><td><code>tushare_fetcher/core/db.py</code><br><code>akshare_fetcher/core/db.py</code></td><td>50+60</td><td>继承 BaseDatabaseManager,覆写配置路径</td></tr>
|
||
<tr><td>RetryManager</td><td><code>findatahub_core/retry.py</code></td><td>~80</td><td>指数退避 + 熔断 + 失败队列</td></tr>
|
||
<tr><td rowspan="8"><strong>L2 预处理</strong></td><td>DataCleaner</td><td><code>findatahub_core/cleaner.py</code></td><td>~200</td><td>5 个子步骤(类型修正/去重/字段清洗/OHLC/AK修复)</td></tr>
|
||
<tr><td>DataImputer</td><td><code>findatahub_core/imputer.py</code></td><td>~80</td><td>3 种策略(ffill/linear/median)</td></tr>
|
||
<tr><td>OutlierDetector</td><td><code>findatahub_core/outlier.py</code></td><td>~70</td><td>IQR/MAD 检测 + 标记列</td></tr>
|
||
<tr><td>StatsAnalyzer</td><td><code>findatahub_core/stats.py</code></td><td>~100</td><td>描述性统计 + 缺失率 + 分布特征</td></tr>
|
||
<tr><td>CorrelationAnalyzer</td><td><code>findatahub_core/correlation.py</code></td><td>~120</td><td>Pearson/Spearman + 冗余检测</td></tr>
|
||
<tr><td>PCAProcessor</td><td><code>findatahub_core/pca.py</code></td><td>~90</td><td>PCA 降维 + Normalizer(3 种)</td></tr>
|
||
<tr><td>QualityGate</td><td><code>findatahub_core/gate.py</code></td><td>~90</td><td>4 级门控 + BLOCK/WARN/INFO</td></tr>
|
||
<tr><td>CrossValidator</td><td><code>findatahub_core/cross_validator.py</code></td><td>~150</td><td>OHLCV 8 字段双源对比</td></tr>
|
||
<tr><td rowspan="4"><strong>L3 使用</strong></td><td>Python SDK</td><td><code>findatahub/client.py</code></td><td>~330</td><td>11 个方法(daily/weekly/monthly/fina/search/export 等)</td></tr>
|
||
<tr><td>REST API</td><td><code>findatahub/api.py</code></td><td>~270</td><td>14 个端点(含健康监控)</td></tr>
|
||
<tr><td>Skills 层</td><td><code>findatahub/skill.md</code></td><td>—</td><td>5 表 schema + 查询示例</td></tr>
|
||
<tr><td>MCP 服务</td><td><code>findatahub/mcp_server.py</code></td><td>~210</td><td>7 个 MCP 工具(搜索/日线/财务/质量/统计等)</td></tr>
|
||
<tr><td rowspan="4"><strong>基础设施</strong></td><td>Pipeline</td><td><code>findatahub_core/pipeline.py</code></td><td>~340</td><td>DAG 编排 + 并行执行 + 反馈回路</td></tr>
|
||
<tr><td>BaseDatabaseManager</td><td><code>findatahub_core/db.py</code></td><td>~280</td><td>连接池 + 幂等 upsert + safe_identifier</td></tr>
|
||
<tr><td>ProfileLoader</td><td><code>findatahub_core/profile.py</code></td><td>~50</td><td>YAML 策略画像加载(3 种)</td></tr>
|
||
<tr><td>HealthMonitor</td><td><code>findatahub_core/health.py</code></td><td>~60</td><td>API 耗时/成功率 + REST 端点</td></tr>
|
||
<tr><td rowspan="3"><strong>P5 云端</strong></td><td>CloudSyncWorker</td><td><code>flows/cloud_sync.py</code></td><td>~180</td><td>全量 + 增量同步 to RDS</td></tr>
|
||
<tr><td>数据版本</td><td><code>_data_versions</code></td><td>—</td><td>版本号标记 + 回滚查询</td></tr>
|
||
<tr><td>数据血缘</td><td><code>_data_lineage</code></td><td>—</td><td>Pipeline Step 自动记录行数/耗时</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ═══════════ 架构重构 ═══════════ -->
|
||
<h2>六、架构重构摘要</h2>
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>重构项</th><th>效果</th><th>状态</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>删除 alpha-common 废弃模块</td><td>减少 4 文件,消除混淆</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>两个 fetcher db.py 继承 BaseDatabaseManager</td><td>670 行 → 110 行(减少 83%)</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>bootstrap.py 移除反向依赖</td><td>核心层 0 import fetcher</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>FetchStepProtocol 协议</td><td>采集器通过接口注入 Pipeline</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>pyproject.toml 补声明</td><td>findatahub-client → findatahub-core</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>Docs 文档整理</td><td>10 文件 → 3 分类目录 + 3 README</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>Tests/ 提升到根级</td><td>5 端到端用例全部通过</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
<tr><td>设计文档合并</td><td>14 文档 → 10 (减少 29%)</td><td><span class="tag tag-ok">DONE</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ═══════════ 目录结构 ═══════════ -->
|
||
<h2>七、最终目录结构</h2>
|
||
|
||
<pre>FinDataHub/
|
||
├── App/ # macOS SwiftUI 桌面应用(29 文件)
|
||
├── Docs/ # 文档中心
|
||
│ ├── README.md # 总索引
|
||
│ ├── pipeline/ # 数据管道架构
|
||
│ │ ├── findatahub-data-pipeline.html # 架构设计 + 规范(合并)
|
||
│ │ ├── rate_limit_pull_plan.md
|
||
│ │ └── EXECUTION_PLAN.md # 已归档
|
||
│ ├── macos/ # macOS App 设计
|
||
│ │ ├── DESIGN.md # HIG 设计 + Liquid Glass(合并)
|
||
│ │ └── DEVLOG.md
|
||
│ └── tracking/ # 项目管理
|
||
│ └── TODO.md # 待办 + P1-P4 完成记录(合并)
|
||
├── Services/ # 6 个微服务
|
||
│ ├── tushare_fetcher/ # TuShare 采集(db.py 50 行)
|
||
│ ├── akshare_fetcher/ # AKShare 采集(db.py 60 行)
|
||
│ ├── findatahub-core/ # 核心预处理(18 模块 + 6 flows)
|
||
│ ├── findatahub-client/ # SDK + API + MCP
|
||
│ ├── kuaixun-service/ # 快讯 API
|
||
│ └── kuaixun-frontend/ # 快讯 Vue.js 前端
|
||
├── Tests/ # 跨层集成测试(5 用例)
|
||
├── Scripts/ # 运维部署脚本
|
||
└── implementation-report/ # 本报告</pre>
|
||
|
||
<!-- ═══════════ 依赖方向 ═══════════ -->
|
||
<h2>八、架构合规状态</h2>
|
||
|
||
<div class="callout success">
|
||
<strong>核心铁律:依赖单向流动(采集层 → 核心层 → 使用层)</strong><br>
|
||
✅ 12 个核心模块 0 反向依赖<br>
|
||
✅ FetchStepProtocol 协议(依赖倒置)<br>
|
||
✅ pyproject.toml 全部声明<br>
|
||
✅ 两个 fetcher db.py 继承 BaseDatabaseManager<br>
|
||
✅ bootstrap.py 使用 BaseDatabaseManager 直连
|
||
</div>
|
||
|
||
<div class="metrics">
|
||
<div class="metric"><div class="num">0</div><div class="lbl">反向依赖</div></div>
|
||
<div class="metric"><div class="num">6</div><div class="lbl">Service(删1)</div></div>
|
||
<div class="metric"><div class="num">3</div><div class="lbl">策略画像</div></div>
|
||
<div class="metric"><div class="num">14.8GB</div><div class="lbl">RDS 同步</div></div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer>
|
||
<p>FinDataHub · Implementation Analysis Report · 2026-08-09 · v1.0</p>
|
||
</footer>
|
||
|
||
<script src="./_shared/js/echarts.min.js"></script>
|
||
<script src="assets/charts.js"></script>
|
||
|
||
</body>
|
||
</html> |