diff --git a/.gitignore b/.gitignore index 705b3e1..f438ade 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ __pycache__/ *.py[cod] *.pyo *.pyd +*.egg-info/ .pytest_cache/ .mypy_cache/ diff --git a/README.md b/README.md index 7d98364..3dec552 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,33 @@ # lhbfx -`lhbfx` 是一个围绕 A 股龙虎榜数据构建的“顶级游资监控系统”,当前聚焦盘后分析、游资席位跟踪、关注池管理、预警提示与个股复盘。 +`lhbfx` 是一个围绕 A 股龙虎榜数据构建的盘后游资监控系统。当前工程聚焦龙虎榜导入、游资席位匹配、关注池管理、个股复盘、风险预警、盘后自动更新以及 PDF/邮件日报。 ## 项目结构 -- `backend/` - - FastAPI 服务 - - 龙虎榜导入、席位映射、预警生成脚本 - - MySQL schema 与查询逻辑 -- `frontend/` - - Vue 3 + TypeScript + Vite 单页应用 - - 首页总控台、游资详情、个股详情、预警中心 -- `docs/` - - 需求文档 - - 技术文档 - - 历史说明与 UI 草稿 +```text +lhbfx/ +├─ backend/ # FastAPI 后端、数据导入脚本、MySQL schema +│ ├─ config.example.yaml # 配置模板 +│ ├─ config.yaml # 本地运行配置 +│ ├─ scripts/ # 命令行脚本入口 +│ └─ src/lhbfx/ # 后端应用源码 +├─ frontend/ # Vue 3 + TypeScript + Vite 前端 +│ ├─ public/ +│ └─ src/ +├─ docs/ # 需求、技术与 UI 文档 +├─ logs/ # 本地运行日志,已忽略 +└─ start-dev.ps1 # 一键启动前后端 +``` -## 当前核心能力 +## 当前能力 -- 首页支持“关注池与操作流水”联动展示。 -- 待加入关注列表支持按买卖结构着色、净额重点突出、快速加入关注。 -- 关注池写入数据库,支持新增和删除。 -- 个股详情支持 K 线、MA5、买卖点与预警信息展示。 -- 预警中心支持卖出预警、慢流出观察等风险信息查看。 -- 已明确新增“每日 17:00 自动更新 + 邮件日报 + PDF 附件”需求,待后续实现。 - -## 最近界面与数据调整 - -- 个股详情页的“买卖明细”已改为“买卖力度趋势”图: - - 柱形图按日期展示买入和卖出,买入为正、卖出为负。 - - 折线展示“当日净额”和“累计净额”。 - - 明细仅在鼠标悬浮图表时显示。 -- 个股详情页顶部新增“预警列表”按钮: - - 有预警时红色高亮提示。 - - 点击后以弹层方式展示当前个股预警,不再挤占右侧图表区域。 -- 个股详情、首页候选股、游资详情页的数据补全逻辑已增强: - - 优先读取数据库中的股票元数据。 - - 外部快照失败时增加备用行情源兜底。 - - 收盘后更新流程会同步补全行业、市值、流通市值等字段。 -- 后端 API 启动时会自动检查最近龙虎榜数据: - - 如果数据库最新交易日已经是当天,则跳过。 - - 如果发现最近工作日数据缺失,会尝试自动补齐并重新生成预警。 - - 检查失败不会阻塞 API 启动,避免服务不可用。 -- 后端服务内置收盘后调度: - - 工作日 17:00 之后会自动尝试执行收盘后更新。 - - 如果 17:00 时数据源尚未准备好,会按固定间隔重试,直到当天数据补齐。 - - 本地和服务器使用同一套调度逻辑,不再依赖本机 Codex 自动任务。 +- 首页总控台:按日期、游资、买卖方向筛选,联动关注池与候选关注股票。 +- 关注池:支持新增、取消关注,并按股票、日期、游资、席位合并展示流水。 +- 个股详情:展示日 K、MA5、买卖点、游资参与概览、买卖力度趋势与预警弹层。 +- 游资详情:展示游资档案、席位、历史参与股票与净额变化。 +- 预警中心:展示卖出预警、慢流出观察等风险信号。 +- 盘后流程:支持导入指定交易日数据、重匹配游资、生成预警、生成 PDF 日报并可发送邮件。 +- 后端调度:API 启动后会按工作日 17:00 之后的窗口尝试补齐当日数据。 ## 环境要求 @@ -55,37 +37,27 @@ ## 后端启动 -1. 复制配置文件: - - 将 `backend/config.example.yaml` 复制为 `backend/config.yaml` - - 按实际数据库连接信息修改 - - 当前仓库已允许提交 `backend/config.yaml`,服务器部署默认读取该文件 +1. 准备配置: + +```powershell +Copy-Item backend\config.example.yaml backend\config.yaml +``` + +按实际 MySQL、邮件等信息修改 `backend/config.yaml`。 + 2. 初始化数据库: ```powershell -python backend/scripts/init_db.py +python backend\scripts\init_db.py ``` 3. 启动 API: ```powershell -python backend/scripts/run_api.py +python backend\scripts\run_api.py ``` -4. 收盘后更新并发送邮件: - -```powershell -python backend/scripts/after_market_update.py --trade-date 2026-04-17 --send-email -``` - -默认约定: - -- 按 `Asia/Shanghai` 取当天日期 -- 预期定时为 A 股交易日 `17:00` -- 只有当日成功拉到新的龙虎榜数据,才会继续生成预警、PDF 并发邮件;否则自动跳过 - -默认地址: - -- `http://127.0.0.1:8000` +默认地址:`http://127.0.0.1:8000`。 ## 前端启动 @@ -95,49 +67,78 @@ npm install npm run dev -- --host 127.0.0.1 --port 5173 ``` -默认地址: - -- `http://127.0.0.1:5173` +默认地址:`http://127.0.0.1:5173`,开发服务器会把 `/api` 代理到后端。 ## 一键开发启动 -根目录提供了 `start-dev.ps1`: +在项目根目录运行: ```powershell powershell -ExecutionPolicy Bypass -File .\start-dev.ps1 ``` -脚本会同时拉起: +脚本会同时启动: -- 后端:`127.0.0.1:8000` -- 前端:`127.0.0.1:5173` +- 后端:`http://127.0.0.1:8000` +- 前端:`http://127.0.0.1:5173` +- 日志:`logs/` -日志输出目录: +## 常用脚本 -- `logs/` +```powershell +# 导入同花顺指定日期龙虎榜数据 +python backend\scripts\import_ths_daily.py --trade-date 2026-04-17 -## 常用后端接口 +# 重跑游资席位匹配 +python backend\scripts\rematch_traders.py + +# 重新生成预警 +python backend\scripts\generate_warnings.py + +# 盘后更新,可选邮件发送 +python backend\scripts\after_market_update.py --trade-date 2026-04-17 --send-email + +# 生成日报,可选发送 +python backend\scripts\daily_report.py --trade-date 2026-04-17 --send +``` + +## 常用 API - `GET /api/summary` - `GET /api/pipeline/status` - `GET /api/traders` - `GET /api/traders/{trader_id}` +- `GET /api/stocks/search` - `GET /api/stocks/{stock_code}` - `GET /api/actions` - `GET /api/watchlist` - `POST /api/watchlist` - `DELETE /api/watchlist/{stock_code}` - `GET /api/warnings` +- `POST /api/pipeline/rematch-traders` +- `POST /api/pipeline/generate-warnings` +- `POST /api/pipeline/refresh-trade-date` + +## 构建与检查 + +```powershell +# 前端类型检查与生产构建 +cd frontend +npm run build + +# 后端语法检查 +cd .. +python -m compileall backend\src backend\scripts +``` ## 文档索引 - 需求文档:`docs/需求文档.md` - 技术文档:`docs/技术文档.md` +- UI 设计说明:`docs/UI设计说明.md` - 历史需求梳理:`docs/需求梳理-今日游资操作优先.md` -- 历史 UI 说明:`docs/UI设计说明.md` +- 完整早期需求:`docs/顶级游资监控系统需求文档.md` ## 仓库说明 -- 仓库已补充 `.gitignore` 与 `.gitattributes` -- 本地敏感配置 `backend/config.yaml` 已忽略,不会推送 -- 提交前建议使用 `backend/config.example.yaml` 作为共享配置模板 +生成物和本地运行产物不进入版本库,包括 `logs/`、`frontend/dist/`、`frontend/node_modules/`、`__pycache__/`、`*.egg-info/`。提交前建议运行前端构建和后端编译检查。 diff --git a/backend/scripts/after_market_update.py b/backend/scripts/after_market_update.py index 4c79596..5ec2c82 100644 --- a/backend/scripts/after_market_update.py +++ b/backend/scripts/after_market_update.py @@ -7,7 +7,7 @@ from _bootstrap import add_src_to_path add_src_to_path() -from lhbfx.after_market import SHANGHAI_TZ, default_trade_date, run_after_market_update +from lhbfx.after_market import default_trade_date, run_after_market_update def parse_args() -> argparse.Namespace: diff --git a/backend/scripts/daily_report.py b/backend/scripts/daily_report.py index a378c09..f7754d3 100644 --- a/backend/scripts/daily_report.py +++ b/backend/scripts/daily_report.py @@ -1,7 +1,6 @@ from __future__ import annotations import argparse -from pathlib import Path from _bootstrap import add_src_to_path diff --git a/backend/src/lhbfx/app.py b/backend/src/lhbfx/app.py index 16d3d25..070750e 100644 --- a/backend/src/lhbfx/app.py +++ b/backend/src/lhbfx/app.py @@ -19,6 +19,7 @@ from .pipeline import ( ) from .queries import ( fetch_stock_detail, + search_stocks, fetch_summary, fetch_trader_actions, fetch_trader_detail, @@ -128,6 +129,12 @@ def api_trader_detail(trader_id: int): return data +@app.get("/api/stocks/search") +def api_stock_search(q: str, limit: int = 8): + safe_limit = min(max(limit, 1), 20) + return search_stocks(q, limit=safe_limit) + + @app.get("/api/stocks/{stock_code}") def api_stock_detail(stock_code: str): data = fetch_stock_detail(stock_code) diff --git a/backend/src/lhbfx/queries.py b/backend/src/lhbfx/queries.py index 67bab8b..4656c76 100644 --- a/backend/src/lhbfx/queries.py +++ b/backend/src/lhbfx/queries.py @@ -176,6 +176,43 @@ def fetch_watchlist(include_archived: bool = False) -> list[dict[str, Any]]: return [_normalize_row(row) for row in cursor.fetchall()] +def search_stocks(query: str, limit: int = 8) -> list[dict[str, Any]]: + keyword = query.strip() + if not keyword: + return [] + + prefix = f"{keyword}%" + contains = f"%{keyword}%" + + with db_cursor() as (_, cursor): + cursor.execute( + """ + SELECT + stock_code, + stock_name, + market, + industry + FROM stocks + WHERE stock_code LIKE %s + OR stock_name LIKE %s + OR stock_name LIKE %s + ORDER BY + CASE + WHEN stock_code = %s THEN 0 + WHEN stock_name = %s THEN 1 + WHEN stock_code LIKE %s THEN 2 + WHEN stock_name LIKE %s THEN 3 + ELSE 4 + END, + updated_at DESC, + stock_code + LIMIT %s + """, + (prefix, prefix, contains, keyword, keyword, prefix, prefix, limit), + ) + return [_normalize_row(row) for row in cursor.fetchall()] + + def upsert_watchlist_item( stock_code: str, stock_name: str, diff --git a/backend/src/lhbfx/reporting.py b/backend/src/lhbfx/reporting.py index 911e5d0..ed1fa3e 100644 --- a/backend/src/lhbfx/reporting.py +++ b/backend/src/lhbfx/reporting.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -from collections import defaultdict from dataclasses import dataclass from pathlib import Path from typing import Any diff --git a/backend/src/lhbfx/scheduler.py b/backend/src/lhbfx/scheduler.py index 87c12fd..66826a9 100644 --- a/backend/src/lhbfx/scheduler.py +++ b/backend/src/lhbfx/scheduler.py @@ -4,7 +4,6 @@ import logging import threading import time from datetime import datetime -from typing import Any from .after_market import SHANGHAI_TZ, run_after_market_update from .config import AppConfig, load_config diff --git a/docs/技术文档.md b/docs/技术文档.md index e14ca38..5d0d354 100644 --- a/docs/技术文档.md +++ b/docs/技术文档.md @@ -2,31 +2,27 @@ ## 1. 技术栈 -### 1.1 前端 - -- Vue 3 -- TypeScript -- Vite - -### 1.2 后端 - -- FastAPI -- PyMySQL -- PyYAML -- Requests / BeautifulSoup - -### 1.3 数据库 - -- MySQL +- 前端:Vue 3、TypeScript、Vite +- 后端:FastAPI、PyMySQL、PyYAML、Requests、BeautifulSoup、ReportLab +- 数据库:MySQL 8+ +- 运行脚本:PowerShell、Python scripts ## 2. 工程结构 ```text -longhubang/ +lhbfx/ ├─ backend/ │ ├─ config.example.yaml +│ ├─ config.yaml │ ├─ pyproject.toml │ ├─ scripts/ +│ │ ├─ init_db.py +│ │ ├─ run_api.py +│ │ ├─ import_ths_daily.py +│ │ ├─ rematch_traders.py +│ │ ├─ generate_warnings.py +│ │ ├─ after_market_update.py +│ │ └─ daily_report.py │ └─ src/lhbfx/ ├─ frontend/ │ ├─ package.json @@ -34,307 +30,115 @@ longhubang/ │ └─ src/ ├─ docs/ ├─ start-dev.ps1 -├─ .gitignore └─ README.md ``` ## 3. 后端架构 -### 3.1 入口 +### 3.1 应用入口 -- `backend/scripts/run_api.py` -- `backend/src/lhbfx/app.py` - -`app.py` 提供 API 路由与前端静态文件挂载能力。 +- `backend/scripts/run_api.py`:命令行启动入口。 +- `backend/src/lhbfx/app.py`:FastAPI 应用、API 路由、静态前端挂载、启动时数据检查与调度启动。 ### 3.2 配置 -- 运行时配置文件:`backend/config.yaml` -- 示例配置文件:`backend/config.example.yaml` +- `backend/config.example.yaml`:共享配置模板。 +- `backend/config.yaml`:本地运行配置,包含数据库、邮件、调度等信息。 +- `backend/src/lhbfx/config.py`:配置模型与加载逻辑。 -为避免泄露本地敏感信息,实际 `config.yaml` 已加入 `.gitignore`。 +### 3.3 数据访问 -### 3.3 数据层 +- `backend/src/lhbfx/db.py`:MySQL 连接与 cursor 上下文。 +- `backend/src/lhbfx/schema.sql`:表结构。 +- `backend/src/lhbfx/queries.py`:面向 API 的查询与聚合,包含摘要、游资详情、个股详情、关注池、股票搜索、预警查询等。 -数据库相关逻辑集中在: +### 3.4 数据导入与处理 -- `backend/src/lhbfx/db.py` -- `backend/src/lhbfx/schema.sql` -- `backend/src/lhbfx/queries.py` +- `backend/src/lhbfx/pipeline.py`:龙虎榜导入、游资席位匹配、预警生成、指定交易日刷新。 +- `backend/src/lhbfx/sources/tonghuashun.py`:同花顺龙虎榜数据源。 +- `backend/src/lhbfx/sources/eastmoney.py`:东方财富行情、K 线与个股资料兜底数据源。 +- `backend/src/lhbfx/sources/tencent.py`:腾讯行情快照兜底数据源。 +- `backend/src/lhbfx/sources/sina.py`:新浪 K 线兜底数据源。 -当前主要表: +### 3.5 盘后任务与日报 -- `stocks` -- `lhb_overview` -- `lhb_detail_seats` -- `warning_events` -- `watchlist_entries` -- `traders` -- `trader_seats` - -### 3.4 数据处理管线 - -`backend/src/lhbfx/pipeline.py` 负责: - -- 龙虎榜日度导入 -- 席位匹配 -- 预警生成 -- 管线状态统计 - -配套脚本位于 `backend/scripts/`: - -- `init_db.py` -- `import_ths_daily.py` -- `import_ths_year.py` -- `generate_warnings.py` -- `rematch_traders.py` - -### 3.5 首页关键接口 - -#### `/api/actions` - -用途: - -- 为首页左侧关注池流水和右侧候选区提供原始操作数据 - -当前返回字段包括: - -- 股票代码、股票名称 -- 游资名称 -- 买入、卖出、净额 -- 当前价格、涨跌幅 -- 行业、市场、总市值、流通市值 -- 操作方向 - -#### `/api/watchlist` - -用途: - -- 保存和读取用户关注池 - -行为: - -- `GET /api/watchlist`:获取当前关注池 -- `POST /api/watchlist`:加入关注 -- `DELETE /api/watchlist/{stock_code}`:取消关注并删除记录 +- `backend/src/lhbfx/after_market.py`:盘后更新编排,负责导入、匹配、预警、日报与邮件。 +- `backend/src/lhbfx/scheduler.py`:API 常驻调度,工作日 17:00 后尝试补齐当日数据。 +- `backend/src/lhbfx/reporting.py`:日报数据聚合与邮件正文。 +- `backend/src/lhbfx/pdf_export.py`:PDF 日报生成。 +- `backend/src/lhbfx/mailer.py`:邮件发送。 ## 4. 前端架构 -### 4.1 前端入口 +### 4.1 入口与状态 -- `frontend/src/main.ts` -- `frontend/src/App.vue` +- `frontend/src/main.ts`:Vue 应用入口。 +- `frontend/src/App.vue`:页面路由状态、股票搜索、全局数据初始化。 +- `frontend/src/composables/useDashboardData.ts`:统一封装 API 调用、选择状态、关注池派生数据与页面数据加载。 +- `frontend/src/types.ts`:API 响应类型。 -`App.vue` 负责页面切换、初始化数据和页面级事件绑定。 +### 4.2 页面组件 -### 4.2 数据管理 +- `AppHero.vue`:顶部状态、导航与股票搜索。 +- `HomeControlScreen.vue`:首页总控台,包含关注池流水与候选关注股票。 +- `TraderDetailScreen.vue`:游资详情。 +- `StockDetailScreen.vue`:个股详情容器、基础信息、预警入口、游资参与概览。 +- `StockDetailKlinePanel.vue`:日 K、MA5、买卖点与预警标记。 +- `StockActionTimelineChart.vue`:买卖力度趋势图。 +- `WarningCenterScreen.vue`:预警中心。 +- `home/CandidateWatchCard.vue`:候选关注股票卡片。 -`frontend/src/composables/useDashboardData.ts` 是首页和多页面共享数据的核心数据层,负责: +### 4.3 工具模块 -- 获取摘要数据 -- 获取预警数据 -- 获取游资列表 -- 获取首页操作流水 -- 获取和维护关注池 +- `frontend/src/utils/format.ts`:金额、涨跌、日期和预警标签格式化。 +- `frontend/src/utils/stockDetailKline.ts`:个股 K 线面板的数据转换与坐标计算。 -### 4.3 页面组件 +## 5. 主要数据流 -- `HomeControlScreen.vue`:首页总控台 -- `TraderDetailScreen.vue`:游资详情,采用单列全宽股票列表,支持时间、名称、净额连续增大筛选,并展示行业、板块、总市值、净额和预警标签 -- `StockDetailScreen.vue`:个股详情 -- `WarningCenterScreen.vue`:预警中心 +1. 用户启动后端,`app.py` 加载配置、创建 FastAPI 应用,并启动盘后调度。 +2. 前端 `useDashboardData.initialize()` 拉取摘要、状态、游资、预警、关注池和操作流水。 +3. 首页筛选条件变化后,前端重新请求 `/api/actions`,并在本地派生关注池流水、候选关注列表和指标。 +4. 用户选择个股时,前端请求 `/api/stocks/{stock_code}`,后端聚合数据库数据、K 线、行情快照、游资操作和预警。 +5. 盘后任务导入龙虎榜数据后,执行席位匹配、预警生成、日报生成与邮件发送。 -首页当前拆分重点组件: +## 6. API 概览 -- `frontend/src/components/home/CandidateWatchCard.vue` +- `GET /api/summary`:系统摘要。 +- `GET /api/pipeline/status`:导入与处理状态。 +- `GET /api/traders`:游资列表。 +- `GET /api/traders/{trader_id}`:游资详情。 +- `GET /api/stocks/search`:股票搜索。 +- `GET /api/stocks/{stock_code}`:个股详情。 +- `GET /api/actions`:游资操作流水。 +- `GET /api/watchlist`:关注池列表。 +- `POST /api/watchlist`:加入关注池。 +- `DELETE /api/watchlist/{stock_code}`:移出关注池。 +- `GET /api/warnings`:预警列表。 +- `POST /api/pipeline/rematch-traders`:重跑席位匹配。 +- `POST /api/pipeline/generate-warnings`:重生成预警。 +- `POST /api/pipeline/refresh-trade-date`:刷新指定交易日。 -### 4.4 首页展示逻辑 +## 7. 生成物与清理规则 -#### 左侧关注池与操作流水 - -在前端中按以下维度合并: - -- 股票 -- 日期 -- 游资 -- 标准化席位名 - -用于解决同一席位同时出现在买入榜与卖出榜时被拆成两条的问题。 - -#### 右侧待加入关注 - -候选列表卡片设计目标: - -- 固定高度 -- 同屏显示更多 -- 信息完整 -- 净额优先 - -## 5. 启动与运行 - -### 5.1 一键启动 - -使用根目录脚本: - -```powershell -powershell -ExecutionPolicy Bypass -File .\start-dev.ps1 -``` - -### 5.2 手动启动 - -后端: - -```powershell -python backend/scripts/run_api.py -``` - -前端: - -```powershell -cd frontend -npm install -npm run dev -- --host 127.0.0.1 --port 5173 -``` - -## 6. 仓库管理建议 - -### 6.1 已加入忽略项 +以下目录或文件属于生成物、本地依赖或运行产物,不应提交: - `frontend/node_modules/` - `frontend/dist/` - `logs/` -- `output/` -- `.playwright-cli/` -- `backend/config.yaml` +- `__pycache__/` +- `*.pyc` +- `*.egg-info/` -### 6.2 行尾与文本规则 +清理时优先删除这些生成物;源码删除必须先确认无引用、无入口、无文档约定。 -仓库已添加 `.gitattributes`: +## 8. 验证命令 -- 默认文本文件统一由 git 管理行尾 -- Windows 脚本保留 `crlf` +```powershell +# 前端类型检查与构建 +cd frontend +npm run build -## 7. 测试与验证建议 - -当前建议至少执行三类验证: - -### 7.1 静态验证 - -- `npm run build` -- `python -m compileall backend/src` - -### 7.2 接口验证 - -- `GET /api/actions` -- `GET /api/watchlist` -- `GET /api/stocks/{stock_code}` - -### 7.3 页面验证 - -建议引入或固化以下检查: - -- 首页强制刷新后布局是否完整 -- 关注池流水是否正确合并 -- 候选卡片是否在固定高度内完整显示 -- 关键页面至少保留一份 Playwright 截图回归 - -## 8. 当前技术债 - -目前仍需持续处理的问题: - -- 历史文档与部分配置存在中文乱码 -- 部分来源数据原始字段编码不稳定 -- 页面样式近期经历多轮快速调整,仍建议补视觉回归测试 - -## 9. 定时更新与邮件报送方案 - -为满足“每天下午 17:00 自动更新并发送盘后邮件”的新增需求,建议增加一个独立的调度与报送模块。 - -### 9.1 调度方式 - -建议采用以下任一方式: - -- 服务器 `cron` -- Windows 任务计划程序 -- 后续统一接入独立任务调度器 - -默认调度时间: - -- 每天下午 `17:00` - -### 9.2 推荐执行流程 - -每日任务执行顺序建议如下: - -1. 拉取当日龙虎榜与行情数据 -2. 更新数据库 -3. 重新生成预警数据 -4. 统计关注池情况 -5. 统计待加入关注候选列表 -6. 生成 PDF 日报 -7. 发送邮件正文与附件 -8. 记录执行日志 - -### 9.3 建议新增模块 - -建议新增以下能力: - -- `backend/scripts/daily_report.py` - - 串联数据更新、统计、PDF 生成、邮件发送 -- `backend/src/lhbfx/reporting.py` - - 负责报表数据整理 -- `backend/src/lhbfx/mailer.py` - - 负责 SMTP 或邮件服务发送 -- `backend/src/lhbfx/pdf_export.py` - - 负责 PDF 生成 - -### 9.4 配置项建议 - -建议在配置文件中新增: - -- 是否启用邮件报送 -- SMTP 主机 -- SMTP 端口 -- 发件人账号 -- 发件人密码或授权码 -- 收件人列表 -- 抄送列表 -- 日报输出目录 -- 调度时间 - -### 9.5 PDF 生成建议 - -PDF 附件可以通过以下方式生成: - -- 基于 HTML 模板渲染后导出 PDF -- 或直接使用 Python PDF 库生成结构化报告 - -推荐内容结构: - -1. 标题页 -2. 数据更新时间 -3. 关注池总览 -4. 关注池流水摘要 -5. 今日待加入关注列表 -6. 风险与预警摘要 - -### 9.6 邮件正文建议 - -正文采用简洁摘要形式,便于手机查看: - -- 今日关注池概况 -- 今日关注池重点动作 -- 今日待加入关注候选 -- 风险提示 -- 附件说明 - -### 9.7 测试建议 - -新增该需求后,应补充以下验证: - -- 17:00 定时任务是否被正确触发 -- 更新失败时是否生成错误日志 -- PDF 是否成功生成 -- 邮件正文是否包含关键摘要 -- 附件是否能正常打开 -- 多收件人场景是否发送成功 +# 后端语法检查 +cd .. +python -m compileall backend\src backend\scripts +``` diff --git a/frontend/src/App.vue b/frontend/src/App.vue index d815433..adb6593 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -7,7 +7,7 @@ import StockDetailScreen from './components/StockDetailScreen.vue' import TraderDetailScreen from './components/TraderDetailScreen.vue' import WarningCenterScreen from './components/WarningCenterScreen.vue' import { useDashboardData } from './composables/useDashboardData' -import type { WarningItem } from './types' +import type { StockSearchItem, WarningItem } from './types' const dashboard = useDashboardData() @@ -15,6 +15,11 @@ type PageKey = 'home' | 'trader' | 'stock' | 'warning' const currentPage = shallowRef('home') const selectedTraderId = computed(() => dashboard.selectedTraderId.value) +const stockSearchQuery = shallowRef('') +const stockSearchResults = shallowRef([]) +const stockSearchLoading = shallowRef(false) +let stockSearchTimer: ReturnType | null = null +let latestStockSearchToken = 0 const navItems: Array<{ key: PageKey; label: string }> = [ { key: 'home', label: '首页总控台' }, @@ -64,6 +69,12 @@ async function handleSelectStock(stockCode: string) { navigate('stock') } +async function handleSearchSelectStock(stock: Pick) { + stockSearchQuery.value = stock.stock_name + stockSearchResults.value = [] + await handleSelectStock(stock.stock_code) +} + async function handleSelectWarningInCenter(warning: WarningItem) { await dashboard.selectWarning(warning) } @@ -105,11 +116,47 @@ onMounted(() => { onUnmounted(() => { window.removeEventListener('hashchange', syncPageFromHash) + if (stockSearchTimer !== null) { + window.clearTimeout(stockSearchTimer) + } }) watch(currentPage, (page) => { void ensurePageData(page) }) + +watch(stockSearchQuery, (nextQuery) => { + const keyword = nextQuery.trim() + if (stockSearchTimer !== null) { + window.clearTimeout(stockSearchTimer) + stockSearchTimer = null + } + + if (!keyword) { + stockSearchResults.value = [] + stockSearchLoading.value = false + return + } + + stockSearchTimer = window.setTimeout(() => { + const currentToken = ++latestStockSearchToken + stockSearchLoading.value = true + void dashboard + .searchStocks(keyword, 8) + .then((results) => { + if (currentToken !== latestStockSearchToken) return + stockSearchResults.value = results + }) + .catch(() => { + if (currentToken !== latestStockSearchToken) return + stockSearchResults.value = [] + }) + .finally(() => { + if (currentToken !== latestStockSearchToken) return + stockSearchLoading.value = false + }) + }, 180) +})