feat(sector_radar): enhance sector radar functionality with active moneyflow and detailed metrics
- Introduced ActiveMoneyflowSource to fetch optional active-order flow, enhancing the sector radar's data capabilities. - Updated StockFactRecord and DailyAggregateRecord to include pct_change and active_buy_net_amount_yuan for improved financial insights. - Modified the build process to incorporate active moneyflow data without invalidating main rankings on failure. - Enhanced the HTTP API to return detailed sector history and metrics, including pct_change and active buy metrics for members. - Updated tests to validate the new functionality and ensure data integrity across various scenarios.
This commit is contained in:
@@ -12,6 +12,7 @@ import { SectorRadarPage } from "./sector-radar-page"
|
||||
const navigate = vi.hoisted(() => vi.fn())
|
||||
const useSectorRadarDates = vi.fn()
|
||||
const useSectorRadarRankings = vi.fn()
|
||||
const useSectorRadarHistory = vi.fn()
|
||||
const refetchDates = vi.fn()
|
||||
const refetchRankings = vi.fn()
|
||||
const fetchNextPage = vi.fn()
|
||||
@@ -24,6 +25,7 @@ vi.mock("@tanstack/react-router", () => ({
|
||||
}))
|
||||
|
||||
vi.mock("@/features/sector-radar/api/sector-radar.query", () => ({
|
||||
useSectorRadarHistory: (...args: unknown[]) => useSectorRadarHistory(...args),
|
||||
useSectorRadarDates: () => useSectorRadarDates(),
|
||||
useSectorRadarRankings: (...args: unknown[]) =>
|
||||
useSectorRadarRankings(...args),
|
||||
@@ -91,6 +93,11 @@ const rankingsResponse: RadarRankingsResponse = {
|
||||
rank_percentile: 100,
|
||||
rank_change_days: 1,
|
||||
rank_change: 3,
|
||||
pct_change: -1.25,
|
||||
daily_net_amount_yuan: 1250000000,
|
||||
daily_ratio: -0.02,
|
||||
on_list_count: 3,
|
||||
history_available_days: 10,
|
||||
},
|
||||
{
|
||||
trade_date: "2026-08-28",
|
||||
@@ -111,6 +118,11 @@ const rankingsResponse: RadarRankingsResponse = {
|
||||
rank_percentile: null,
|
||||
rank_change_days: 1,
|
||||
rank_change: null,
|
||||
pct_change: null,
|
||||
daily_net_amount_yuan: null,
|
||||
daily_ratio: null,
|
||||
on_list_count: null,
|
||||
history_available_days: 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -163,6 +175,11 @@ describe("SectorRadarPage", () => {
|
||||
refetch: refetchDates,
|
||||
})
|
||||
useSectorRadarRankings.mockReturnValue(rankingQueryResult())
|
||||
useSectorRadarHistory.mockReturnValue({
|
||||
isPending: false,
|
||||
isError: false,
|
||||
data: { status: "no_data" },
|
||||
})
|
||||
navigate.mockReset()
|
||||
refetchDates.mockReset()
|
||||
refetchRankings.mockReset()
|
||||
@@ -175,14 +192,21 @@ describe("SectorRadarPage", () => {
|
||||
|
||||
expect(screen.getAllByText("+12.5 亿元")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("19/20")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("100%")[0]).toBeInTheDocument()
|
||||
expect(screen.queryByText("19/20")).not.toBeInTheDocument()
|
||||
expect(screen.queryByText("BK0001.DC")).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getAllByRole("columnheader", { name: "排名百分位" }),
|
||||
screen.queryByRole("columnheader", { name: "排名百分位" }),
|
||||
).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByRole("columnheader", { name: "样本" }),
|
||||
).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getAllByRole("columnheader", { name: "涨跌幅" }),
|
||||
).toHaveLength(2)
|
||||
expect(screen.getAllByRole("columnheader", { name: "样本" })).toHaveLength(
|
||||
2,
|
||||
)
|
||||
expect(
|
||||
screen.getAllByRole("columnheader", { name: "单日流入率" }),
|
||||
).toHaveLength(2)
|
||||
expect(screen.getAllByText("-2%")).toHaveLength(2)
|
||||
expect(
|
||||
screen.queryByRole("columnheader", { name: "资金覆盖率" }),
|
||||
).not.toBeInTheDocument()
|
||||
@@ -206,6 +230,66 @@ describe("SectorRadarPage", () => {
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("mirrors ratio columns and expands only the selected sector history, resetting on date change", () => {
|
||||
routeSearch = { ...routeSearch, view: "ratio" }
|
||||
const ratioPage = {
|
||||
...rankingsResponse,
|
||||
view: "ratio",
|
||||
rows: rankingsResponse.rows.map((row) => ({
|
||||
...row,
|
||||
unit: "ratio",
|
||||
metric_value: -0.01,
|
||||
})),
|
||||
}
|
||||
useSectorRadarRankings.mockReturnValue(
|
||||
rankingQueryResult({ data: { pages: [ratioPage], pageParams: [1] } }),
|
||||
)
|
||||
const { rerender } = render(<SectorRadarPage />)
|
||||
expect(screen.getAllByRole("columnheader", { name: "在榜" })).toHaveLength(
|
||||
2,
|
||||
)
|
||||
expect(screen.getAllByText("-1%")).toHaveLength(4)
|
||||
const trigger = screen.getAllByRole("button", {
|
||||
name: "机器人展开在榜历史",
|
||||
})[0]!
|
||||
fireEvent.click(trigger)
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "true")
|
||||
expect(useSectorRadarHistory).toHaveBeenLastCalledWith({
|
||||
sectorType: "concept",
|
||||
sectorCode: "BK0001.DC",
|
||||
tradeDate: "2026-08-28",
|
||||
})
|
||||
expect(screen.getByText("暂无在榜历史数据。")).toBeInTheDocument()
|
||||
fireEvent.click(trigger)
|
||||
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
|
||||
fireEvent.click(trigger)
|
||||
useSectorRadarRankings.mockReturnValue(
|
||||
rankingQueryResult({
|
||||
data: {
|
||||
pages: [
|
||||
{
|
||||
...ratioPage,
|
||||
publication: {
|
||||
...successPublication,
|
||||
publication_id: "next-day",
|
||||
},
|
||||
rows: ratioPage.rows.map((row) => ({
|
||||
...row,
|
||||
trade_date: "2026-08-31",
|
||||
})),
|
||||
},
|
||||
],
|
||||
pageParams: [1],
|
||||
},
|
||||
}),
|
||||
)
|
||||
rerender(<SectorRadarPage />)
|
||||
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getAllByRole("button", { name: "机器人展开在榜历史" })[0],
|
||||
).toHaveAttribute("aria-expanded", "false")
|
||||
})
|
||||
|
||||
it("resets filters to the first batch in router search state", () => {
|
||||
routeSearch = { ...routeSearch, page: 3 }
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
Reference in New Issue
Block a user