diff --git a/apps/web/src/api/products/index.ts b/apps/web/src/api/products/index.ts index b4bf23a95..5f1148b57 100644 --- a/apps/web/src/api/products/index.ts +++ b/apps/web/src/api/products/index.ts @@ -13,6 +13,8 @@ export type { VideoItem, } from "./types" +export type { RecomputeDedupResponse } from "./products" + // 工具函数 export { mapVideoToProductItem } from "./utils" @@ -25,4 +27,5 @@ export { updateReviewStatus, batchDownload, getBatchDownloadStatus, + recomputeDedup, } from "./products" diff --git a/apps/web/src/api/products/products.ts b/apps/web/src/api/products/products.ts index 7c0fcb19f..18680df7b 100644 --- a/apps/web/src/api/products/products.ts +++ b/apps/web/src/api/products/products.ts @@ -78,3 +78,18 @@ export const getBatchDownloadStatus = async (jobId: string): Promise => { + const response = await apiClient.post("/videos/recompute-dedup", { + video_ids: videoIds, + }) + return response.data +} diff --git a/apps/web/src/pages/products/ProductLibrary.tsx b/apps/web/src/pages/products/ProductLibrary.tsx index d4d2ea40d..d15cacc9c 100644 --- a/apps/web/src/pages/products/ProductLibrary.tsx +++ b/apps/web/src/pages/products/ProductLibrary.tsx @@ -11,7 +11,7 @@ * 产品卡片 → components/ProductCard(内联视频播放) */ import React from "react" -import { VideoCameraOutlined, DownloadOutlined } from "@ant-design/icons" +import { VideoCameraOutlined, DownloadOutlined, ReloadOutlined } from "@ant-design/icons" import { Button } from "@/components/ui" import { ProductCard } from "./components/ProductCard" import { ProductFilterBar } from "./components/ProductFilterBar" @@ -19,6 +19,7 @@ import { ProductBatchBar } from "./components/ProductBatchBar" import { ProductEmptyState } from "./components/ProductEmptyState" import { useProductList } from "./hooks/useProductList" import { useProductActions } from "./hooks/useProductActions" +import { useRecomputeDedup } from "./hooks/product-actions/useRecomputeDedup" import "./products.css" const ProductLibrary: React.FC = () => { @@ -67,6 +68,8 @@ const ProductLibrary: React.FC = () => { setPlayingProduct: () => {}, // 不再使用弹窗播放 }) + const { recomputeDedup, isRecomputing } = useRecomputeDedup() + // ── Loading 状态 ── if (isLoading) { return @@ -94,6 +97,15 @@ const ProductLibrary: React.FC = () => { + diff --git a/apps/web/src/pages/products/hooks/product-actions/useRecomputeDedup.ts b/apps/web/src/pages/products/hooks/product-actions/useRecomputeDedup.ts new file mode 100644 index 000000000..40341c7c7 --- /dev/null +++ b/apps/web/src/pages/products/hooks/product-actions/useRecomputeDedup.ts @@ -0,0 +1,27 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query" +import { message } from "antd" +import { recomputeDedup } from "@/api/products" + +export function useRecomputeDedup() { + const queryClient = useQueryClient() + + const mutation = useMutation({ + mutationFn: () => recomputeDedup(), + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["products"] }) + if (data.enqueued > 0) { + message.success(`已提交 ${data.enqueued} 个视频的查重任务,后台处理中`) + } else { + message.info("所有视频查重率已是最新,无需重算") + } + }, + onError: () => { + message.error("查重任务提交失败,请稍后重试") + }, + }) + + return { + recomputeDedup: () => mutation.mutate(), + isRecomputing: mutation.isPending, + } +} diff --git a/apps/web/src/test/pages/ProductLibrary.test.tsx b/apps/web/src/test/pages/ProductLibrary.test.tsx index f2c8629e2..d157b1d04 100644 --- a/apps/web/src/test/pages/ProductLibrary.test.tsx +++ b/apps/web/src/test/pages/ProductLibrary.test.tsx @@ -102,6 +102,7 @@ vi.mock("@ant-design/icons", () => ({ SearchOutlined: () => , ShareAltOutlined: () => , VideoCameraOutlined: () => , + ReloadOutlined: () => , })) vi.mock("@/store/authStore", () => ({