feat: 每日AI分析改造——定调摘要、环比快照、完整连板梯队带涨停原因、生成次数记账

This commit is contained in:
Sakurasan
2026-09-02 02:59:14 +08:00
parent 375d196fef
commit 0fb2f8d3a5
9 changed files with 265 additions and 21 deletions
+2 -1
View File
@@ -143,7 +143,8 @@ async def admin_list_reports(request: Request):
conn = get_connection()
try:
rows = conn.execute(
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, created_at "
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
"generation_count, created_at, updated_at "
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
).fetchall()
items = []
+2 -1
View File
@@ -60,7 +60,8 @@ async def list_reports():
conn = get_connection()
try:
rows = conn.execute(
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, created_at "
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
"generation_count, created_at, updated_at "
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
).fetchall()
items = []
+62
View File
@@ -485,6 +485,67 @@ async def _build_dashboard() -> dict:
"detail": f"涨停 {limit_up_count} 跌停 {limit_down_count} 炸板 {break_count}",
})
# ── 连板梯队(完整版,供 AI 分析用;上方 events 天梯只取前2保持看板精简) ──
# 涨停池里带 limit_up_reason / seal_money,按代码匹配给梯队个股
reason_by_code = {}
if isinstance(limit_up_data, dict):
for lu in limit_up_data.get("item", []) or []:
code = lu.get("thscode", "")
if code:
reason_by_code[code] = {
"reason": (lu.get("limit_up_reason") or "").strip(),
"sealWan": round(_safe_float(lu.get("seal_money")) / 10000),
}
_LADDER_LEVELS = [
("seven_over", 7, "7连板+"),
("six_board", 6, "6连板"),
("five_board", 5, "5连板"),
("four_board", 4, "4连板"),
("three_board", 3, "3连板"),
("two_board", 2, "2连板"),
("first_board", 1, "首板"),
]
limit_ladder = []
if isinstance(limit_ladder_data, dict):
ladder_items = limit_ladder_data.get("item", [])
if ladder_items:
today_boards = ladder_items[0].get("boards", {}) or {}
seen_keys = set()
for key, board_num, label in _LADDER_LEVELS:
seen_keys.add(key)
# 首板数量多(几十家)只取前8家;2连板以上全量保留
cap = 8 if board_num == 1 else None
entries = today_boards.get(key, []) or []
if cap is not None:
entries = entries[:cap]
for item in entries:
code = item.get("thscode", "")
extra = reason_by_code.get(code, {})
limit_ladder.append({
"board": board_num,
"label": label,
"name": item.get("name", ""),
"code": code,
"reason": extra.get("reason", ""),
"sealWan": extra.get("sealWan"),
})
# 兜底:天梯返回了未知层级 key 时也带上(跳过已处理的已知 key)
for key, entries in today_boards.items():
if key in seen_keys:
continue
for item in entries or []:
code = item.get("thscode", "")
extra = reason_by_code.get(code, {})
limit_ladder.append({
"board": _safe_float(item.get("board_num")) or 1,
"label": f"{int(_safe_float(item.get('board_num')) or 1)}连板",
"name": item.get("name", ""),
"code": code,
"reason": extra.get("reason", ""),
"sealWan": extra.get("sealWan"),
})
# ── 组装结果 ──
result = {
"indices": indices,
@@ -492,6 +553,7 @@ async def _build_dashboard() -> dict:
"sectorStrength": sector_strength[:31],
"conceptStrength": concept_strength[:10],
"events": events,
"limitLadder": limit_ladder,
"updateTime": datetime.now(BJT).strftime("%Y-%m-%d %H:%M:%S"),
}