Files
tick-stock-panel/backend/app/services/repair_daily.py
T
wshy ddde2b9653 feat: 数据修正功能 + 盘后管道暂停实时行情防竞态 (#87)
* refactor: 「群机器人 Webhook」统一更名为「群推送 Webhook」

"群机器人"易与后续接入的"智能机器人(API 模式)"混淆。
该通道本质是单向往群聊推送消息, 更名为「群推送 Webhook」更准确。

涉及: 飞书/企业微信的 UI 标签、操作指引、后端文档字符串、
错误提示文案(代码逻辑/接口不变)。覆盖 6 个文件, 纯文案改动。

* feat: 数据修正功能 + 盘后管道暂停实时行情防竞态

数据修正/补数据:
- 数据页顶部新增「修正数据」按钮, 弹窗选起始日期重拉到今天
- 复用盘后管道全流程 (维表/A股日K/除权/enriched/指数), 仅日期由用户传入
- run_now() 加 override_start_date 参数, 注入 A股日K + 指数拉取起点
- 新增 /api/kline/repair_daily 端点 (异步 job + 进度轮询)
- 前端 RepairDailyPanel + DatePicker, 默认起始日期为30天前

实时行情暂停机制 (防写盘竞态):
- QuoteService 加 _paused 标志 + pause()/resume()/paused() 上下文管理器
- 盘后管道/数据修正运行期间自动暂停实时行情取数, 防止覆写同一批 parquet
- toggle 端点: 暂停态下禁止开启实时行情 (409)
- 前端开关: 暂停时 disabled + 显示「数据同步运行中,已临时暂停」
- 三处注入 pause: pipeline.py / kline.py(repair_daily) / daily_pipeline.py(定时)
2026-07-09 13:47:39 +08:00

55 lines
2.0 KiB
Python

"""修正 / 补全日K数据 — 完全复用盘后管道,只是日期范围由用户传入。
典型场景: 昨天没看盘 / 服务挂了一天,本地日K缺了若干天。
设计原则: 不重复盘后管道的任何逻辑。直接调用 daily_pipeline.run_now(),
通过 override_start_date 参数把"自动算日期"换成"用户指定起点",
其余 (维表 / A股日K / 除权因子 / enriched / 指数 / ETF / 错误兜底) 全部原样复用。
这样修正功能与盘后管道永远保持一致,不会出现遗漏。
落盘是 merge-upsert (按 symbol+date 去重 keep="last"), 新数据天然覆盖旧值,
不会产生重复,也不需要先删分区。
"""
from __future__ import annotations
import logging
from collections.abc import Callable
from datetime import date
from app.tickflow.capabilities import CapabilitySet
from app.tickflow.repository import KlineRepository
logger = logging.getLogger(__name__)
def run_repair_daily(
repo: KlineRepository,
capset: CapabilitySet,
start_date: date,
end_date: date | None = None,
on_progress: Callable | None = None,
) -> dict:
"""修正 / 补全数据 — 复用盘后管道,日期范围由用户指定。
通过 run_now(override_start_date=start_date) 把日K/除权/指数的拉取起点
统一设为 start_date (到今天),其余流程与盘后管道完全一致。
Args:
repo: 数据仓库
capset: 权限集
start_date: 用户选定的起始日期
end_date: 保留参数(目前盘后管道固定拉到今天, 此值未使用, 为接口兼容保留)
on_progress: 进度回调
Returns:
run_now() 的完整结果 dict。
"""
today = date.today()
if start_date > today:
return {"error": "起始日期不能晚于今天"}
logger.info("repair_daily: run pipeline with override_start_date=%s", start_date)
from app.jobs.daily_pipeline import run_now
return run_now(repo, capset, on_progress=on_progress, override_start_date=start_date)