Files
PolyWeather/frontend/components/dashboard/scan-terminal/use-city-market-scan.ts
T
2569718930@qq.com b122e7cbae Stabilize the decision workspace data boundaries
The scan terminal had grown into overlapping CSS, request-state, AI-provider, and city-card data responsibilities. This refactor separates those boundaries without changing product behavior: CSS modules are split by surface, city AI prompt/provider/fallback logic is isolated, and scan terminal request state now has reusable RemoteData adapters plus business-state tests.

Constraint: Preserve existing global scan-terminal class names and API responses during the refactor

Constraint: No new dependencies; keep this as a file-boundary cleanup

Rejected: Introduce React Query now | higher migration risk than the requested lightweight query-client path

Rejected: Rewrite AI stream behavior | progressive/fallback states are product-sensitive and were only adapter-split

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Keep AI stream state changes covered by business snapshots before changing fallback/cache wording

Tested: npm run test:business; npx tsc --noEmit; npm run build; python pytest -q; ruff check; py_compile targeted city AI modules

Not-tested: Live DeepSeek provider network replay and browser visual QA
2026-04-28 14:45:34 +08:00

76 lines
1.9 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { scanTerminalClient } from "@/components/dashboard/scan-terminal/scan-terminal-client";
import { useRemoteDataQuery } from "@/components/dashboard/scan-terminal/use-remote-data-query";
import type { CityDetail, MarketScan } from "@/lib/dashboard-types";
import {
deriveCityMarketScanView,
resolveCityMarketScanSnapshot,
writeCachedCityMarketScan,
} from "./market-scan-state";
export function useCityMarketScan({
detail,
detailCityName,
enabled = true,
}: {
detail: CityDetail | null;
detailCityName: string;
enabled?: boolean;
}) {
const {
remote: marketRemote,
reset: resetMarketRemote,
run: runMarketScanQuery,
setSuccess: setMarketScanSuccess,
} = useRemoteDataQuery<MarketScan>();
useEffect(() => {
const snapshot = resolveCityMarketScanSnapshot({
detail,
detailCityName,
enabled,
});
if (snapshot.action === "reset") {
resetMarketRemote();
return;
}
if (snapshot.action === "success") {
setMarketScanSuccess(snapshot.payload);
if (snapshot.shouldWriteCache) {
writeCachedCityMarketScan(snapshot.cacheKey, snapshot.payload);
}
return;
}
void runMarketScanQuery({
request: (signal) =>
scanTerminalClient.getMarketScan(detailCityName, {
lite: false,
signal,
targetDate: detail?.local_date || null,
}),
showLoading: true,
onSuccess: (payload) => {
if (payload) {
writeCachedCityMarketScan(snapshot.cacheKey, payload);
}
},
});
}, [
detail,
detailCityName,
enabled,
resetMarketRemote,
runMarketScanQuery,
setMarketScanSuccess,
]);
const { marketScan, marketStatus } = deriveCityMarketScanView({
detailMarketScan: detail?.market_scan,
marketRemote,
});
return { marketRemote, marketScan, marketStatus };
}