From 76bad64163e8e266b9bbd1a83f4631bd3590d611 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Tue, 28 Apr 2026 12:22:43 +0800 Subject: [PATCH] 1 --- backend/app/__pycache__/main.cpython-313.pyc | Bin 8201 -> 8941 bytes backend/app/main.py | 27 +- frontend/src/app/(auth)/dashboard/page.tsx | 338 +++++++++++-------- 3 files changed, 212 insertions(+), 153 deletions(-) diff --git a/backend/app/__pycache__/main.cpython-313.pyc b/backend/app/__pycache__/main.cpython-313.pyc index ff55f88e5d58c18f7bc6711ccda67e316db093d3..2b3959a4812a88660a22420b4922643ad6e74678 100644 GIT binary patch delta 2065 zcmb7F-A@}w5Z|-U_5m>ljN@-`94s&pJ4r(cEeZ`SO``@7cO*2y&Glk$%_a67**jC< zA*7Wml_*i=oFa-;6}5e-s`jBV60iLiNUMNWR@E0OwNj;g@LH+6c5K`v66#4eH#__L z&Fs$1?k&IZ_ekSYyS*NeC*1c)zU#_2dQ9u)eAf?dp-nj816Ntpo@ft)J_;dE$gnf+ z;V?)X>SM|l6c+40fDR{Ep)p}SVGb6v(ztN+_6mKjObGC49ZllSRgq)G09>FOMBs;X5b45ry< z1LLvBW$$`6keUFPHvaJhUmbVUcqlb%m0VH;MlcEH0duG}(gEYN&Dl@|^8a^S|1y5( zp1+r^gN@k~a31guQVicoCwQkDfE?NKY)j1s$hMw;!r(kp$ujRWHaVxqR2hfBXfFT_ zAOzANY8f-8EFUaIZMaU7Rqc94)*-GZUn!HEf!T${<~=A8TV{T3+iV~BONvLVjdTh(6X+lophAWaLC*M?+59KtXWkoIkmH@WAH8Onu z3}(-cy*-X?Q>Xc{vtwtlg)Aql;kq=`&k+X@VNN3|oP(LD8QT?A))qwS8X^s|ij;-O zO?+y!S3;o>c3zQ^*PxP$OS7}_tRyS3*#+F3EJe>DSfY|uEuknO&^&4e5jR1RZP;Sn z4_J4xy>sox+Kt=k&$G9(>-`&pKMig>hPRszKe4eE>uwV`7+ewaVv&5*yjl!)tcj~) z(H~wLS{*7jdsYJZK(W=oGM*nV`VOu2uJ%55a4zf3(MK)76WMZiZ@Rl5x_dj98$M%; zoMVggKH$7xbo|1#Z*g6lT-SH5hg|%L8F-F7CF=r(4a>UqAU(V2BoWvor=liQ9vdGY z_++VM<+9SenA4z!>xoQ>Nj0bFQ9tri9w|LQN2rKV4-S%;Sn5hFCFv5W3`B8iwLh~G zTLx( z!d3+J98$)jN3l&5Q)*HaQ5$7kG!_HP%qfEnUXGOd8g&pe=0uU64}vPKoF=rK2HyB4 zc&UGij%jpkCWoyQ2`y0D-YU4kteVPY;3;&6OwhlzcHelV?Re8J+rk`u+zptPUx9PG NuBni3(;1c${{=4Fu^a#Z delta 1379 zcmaJ=&2Jk;6yLAdi5=$y*Qv9P<4r^2S`e=&DIpLn$D}DiK!t}?P!^WWdT=(ecf;&% zn~GjQ90(T_1`eDmIDv%3!QA1<2_Y^-qaJcbLJBQ^0P{9YATshu``dZ%_r7Q5&ntV? zp$Dl{Lg4G(kAFB@>HVRsG^Ffb{7x~%dS(A8^H!?91oYW;@u6r93IaE*o@GJ0mgM?c zw;m0m6dGETHSk;E37(t2EFPPYN71k-12{kN-b=z9*aQ z$&UAA)t>A`Pd0zd%Dm>!EHz^w1qCMKAPHkqdPM(|@raD%H| zO8z$uXd%nLVTndtqf6J>qQ359;_H@0JXiOXEm`K=o^;3jopUM zi`3hen&PyN)0DWSM8lc77gRj)`M5kyS^Yfwa#8z#ApYbUs^~ zgjdBx>`VNwX`~9w$_c#O`8@lRI9%c~i{IqMIi5>aI<|UK8eavYv52ufH^94r^EZEc zWR>2Zc>2{S*9&0s2AIepX^|Vz&JPHIyF&&5)&O1vh;b;%1i}6_05I}p4jR-&-_K9= z!Oi)o^HqLN%s${-Au5-}HnG*>=dsN-U(#A(A#;hvO|#+Irpe+hui13mCRw9j6!ye9 zTF~CEvgC&4JNEOQ`>xYueJfXL>sOhw_R8utHqgYuOWXw$)^D1w)yAetqHbWG$D^sy zueBQsS3!n~n9O~m?8E`x7C;q<{-w6p2wS*9KI9^wj^O`7pPah%_@NO0T}b~Xqz`h_ rcPn=*9~VEJ`(*B$Ti@UL_Rc}Faxhr9A6G=_C?O<9JK None: + logging.basicConfig( + level=logging.DEBUG if settings.debug else logging.INFO, + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + # 保留应用日志的调试能力,但压制基础设施层的噪音。 + noisy_loggers = { + "aiosqlite": logging.WARNING, + "sqlalchemy": logging.WARNING, + "sqlalchemy.engine": logging.WARNING, + "sqlalchemy.pool": logging.WARNING, + "httpx": logging.INFO, + "httpcore": logging.WARNING, + "uvicorn.access": logging.INFO, + } + for name, level in noisy_loggers.items(): + logging.getLogger(name).setLevel(level) + + +configure_logging() logger = logging.getLogger(__name__) diff --git a/frontend/src/app/(auth)/dashboard/page.tsx b/frontend/src/app/(auth)/dashboard/page.tsx index 6a4569d4..4b39885b 100644 --- a/frontend/src/app/(auth)/dashboard/page.tsx +++ b/frontend/src/app/(auth)/dashboard/page.tsx @@ -11,8 +11,6 @@ import type { SectorData, StrategyBoard, } from "@/lib/api"; -import MarketTemp from "@/components/market-temp"; -import SectorHeatmap from "@/components/sector-heatmap"; import { ThemeToggle } from "@/components/theme-toggle"; import { useAuth } from "@/hooks/use-auth"; import { useWebSocket } from "@/hooks/use-websocket"; @@ -209,9 +207,7 @@ export default function DashboardPage() { )} -

- 先看今天能不能做,再看该做什么,最后进入执行。 -

+

先看结论,再看动作,再看焦点标的。

@@ -242,17 +238,27 @@ export default function DashboardPage() { observeCount={observe.length} /> -
+
- +
+ +
+ +
- -
- - -
); } @@ -321,63 +322,147 @@ function DecisionHero({ function ActionPanel({ actions, - focusQueue, - fallbackTitle, + summary, }: { actions: ReturnType; - focusQueue: RecommendationData[]; - fallbackTitle: string; + summary: ReturnType; }) { return ( -
-
-
-
-

今天该做什么

-

把市场结论拆成可执行动作,而不是只给情绪描述。

-
- - 系统校准 - -
- -
- - - -
+
+
+ + +
-
-
-
-

{fallbackTitle}

-

只展示最该盯的少量标的,避免首页无限拉长。

-
- - 全部推荐 - -
- -
- {focusQueue.length ? ( - focusQueue.map((rec) => ) - ) : ( -
- 暂无标的,等待新一轮扫描输出。 -
- )} -
+
+ + +
); } -function ExecutionPanel({ - recommendations, +function FocusPanel({ + focusQueue, actionableCount, watchCount, observeCount, +}: { + focusQueue: RecommendationData[]; + actionableCount: number; + watchCount: number; + observeCount: number; +}) { + return ( +
+
+
+

焦点标的

+

首页只保留最该处理的少量标的。

+
+
+ + + +
+
+ +
+ {focusQueue.length ? ( + focusQueue.map((rec) => ) + ) : ( +
+ 今天没有需要处理的焦点标的。 +
+ )} +
+
+ ); +} + +function MarketSnapshot({ + marketTemperature, + indices, + sectors, + summary, +}: { + marketTemperature: MarketTemperatureData | null; + indices: IndexOverview[]; + sectors: SectorData[]; + summary: ReturnType; +}) { + const leadingSectors = sectors.slice(0, 4); + const majorIndices = indices.slice(0, 3); + + return ( +
+
+
+

市场证据

+

只保留对今天决策有用的信息。

+
+ + 温度 {Math.round(marketTemperature?.temperature ?? 0)} + +
+ +
+ + + + +
+ + {majorIndices.length ? ( +
+ {majorIndices.map((item) => ( +
+
{item.name}
+
+ {item.close.toFixed(2)} + = 0 ? "text-red-400" : "text-emerald-400"}`}> + {item.pct_chg >= 0 ? "+" : ""}{item.pct_chg.toFixed(2)}% + +
+
+ ))} +
+ ) : null} + + {leadingSectors.length ? ( +
+
今日盯住的板块
+
+ {leadingSectors.map((sector) => { + const pct = sector.realtime_pct_change ?? sector.pct_change; + return ( +
+
+
{sector.sector_name}
+
+ {sector.limit_up_count > 0 ? `${sector.limit_up_count} 涨停` : "无涨停"} · {sector.stage || "mid"} +
+
+ = 0 ? "text-red-400" : "text-emerald-400"}`}> + {pct >= 0 ? "+" : ""}{pct.toFixed(2)}% + +
+ ); + })} +
+
+ ) : null} + +
+ 结论:{summary.headline} +
+
+ ); +} + +function AdminPanel({ isAdmin, opsStatus, refreshing, @@ -385,10 +470,6 @@ function ExecutionPanel({ onRefresh, onAction, }: { - recommendations: RecommendationData[]; - actionableCount: number; - watchCount: number; - observeCount: number; isAdmin?: boolean; opsStatus: OpsStatusResponse | null; refreshing: boolean; @@ -396,92 +477,43 @@ function ExecutionPanel({ onRefresh: () => void; onAction: (action: "update_tracking" | "generate_strategy_board" | "generate_strategy_iteration") => void; }) { + if (!isAdmin || !opsStatus) return null; + return ( -
-
-

执行入口

-

不同任务进入不同页面,首页只保留关键入口和现状摘要。

- -
- - - - -
- -
-
- 推荐池状态 - {recommendations.length} 只 -
-
- - - -
+
+
+
+

管理员

+

{opsStatus.data_freshness.message}

+ + {opsStatus.scan_running ? "扫描中" : "空闲"} +
- {isAdmin && opsStatus ? ( -
-
-
-

管理员任务中心

-

{opsStatus.data_freshness.message}

-
- - {opsStatus.scan_running ? "扫描中" : "空闲"} - -
+
+ + + + +
-
- - - - -
- -
- - - -
-
- ) : null} +
+ + +
); } @@ -530,6 +562,15 @@ function HeroFact({ label, value }: { label: string; value: string }) { ); } +function CompactBadge({ label, value }: { label: string; value: string }) { + return ( + + {label} + {value} + + ); +} + function ActionBucket({ title, items, @@ -602,12 +643,12 @@ function FocusStockCard({ rec }: { rec: RecommendationData }) { ); } -function NavCard({ href, title, description }: { href: string; title: string; description: string }) { +function EvidenceStat({ label, value, tone }: { label: string; value: number; tone: string }) { return ( - -
{title}
-
{description}
-
+
+
{label}
+
{value}
+
); } @@ -677,6 +718,7 @@ function buildMarketSummary( cannotDo, modeLabel: strategyProfile?.market_stance || board?.recommended_mode || "等待更新", positionLabel: board?.position_suggestion || (strategyProfile?.max_position_pct ? `${strategyProfile.max_position_pct}% 以内` : "等待更新"), + riskLabel: board?.risk_level || "等待更新", }; }