From fdf83a674532fff672436451d74984d333ccdb64 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 23 Aug 2026 10:30:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8C=B9=E9=85=8D=E6=97=A5=E5=BF=97=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=BB=93=E6=9E=9C=E7=8A=B6=E6=80=81=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E4=B8=8E=E6=97=A5=E6=9C=9F=E7=AD=9B=E9=80=89=E5=8F=8A?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worker-platform/product-match-repo.ts | 57 ++++++++++- .../admin-kuaishou-match-service.ts | 20 +++- .../pages/admin/panels/ProductMatchPanel.tsx | 94 +++++++++++++++++-- .../src/services/admin/worker-platform.ts | 8 +- 4 files changed, 160 insertions(+), 19 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform/product-match-repo.ts b/apps/backend/src/repositories/worker-platform/product-match-repo.ts index e4f06cc6..81280e27 100644 --- a/apps/backend/src/repositories/worker-platform/product-match-repo.ts +++ b/apps/backend/src/repositories/worker-platform/product-match-repo.ts @@ -231,16 +231,65 @@ export async function createWorkProductMatchLog(input: { return result.rows[0] || null } -export async function listWorkProductMatchLogs(limit = 100): Promise { +/** 匹配日志分页查询:表本身全量保留,可按结果状态、关键词(商品/SKU/店铺)与日期范围筛选。 */ +export async function listWorkProductMatchLogs({ + page = 1, + pageSize = 20, + matchStatus = '', + keyword = '', + createdFrom = '', + createdTo = '', +}: { + page?: number + pageSize?: number + matchStatus?: string + keyword?: string + createdFrom?: string + createdTo?: string +} = {}): Promise<{ items: WorkProductMatchLogRow[]; total: number }> { + const conditions: string[] = [] + const params: unknown[] = [] + const status = String(matchStatus || '').trim() + if (status) { + params.push(status) + conditions.push(`wpml.match_status = $${params.length}`) + } + const normalizedKeyword = String(keyword || '').trim() + if (normalizedKeyword) { + params.push(`%${normalizedKeyword}%`) + conditions.push( + `(wpml.item_title ILIKE $${params.length} OR wpml.rel_item_id ILIKE $${params.length} OR wpml.rel_sku_id ILIKE $${params.length} OR wpml.sku_nick ILIKE $${params.length} OR wpml.seller_id ILIKE $${params.length})`, + ) + } + if (createdFrom) { + params.push(createdFrom) + conditions.push( + `wpml.created_at >= ($${params.length}::date)::timestamp AT TIME ZONE 'Asia/Shanghai'`, + ) + } + if (createdTo) { + params.push(createdTo) + conditions.push( + `wpml.created_at < (($${params.length}::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')`, + ) + } + const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '' + + const totalResult = await query<{ total: number }>( + `SELECT COUNT(*)::int AS total FROM work_product_match_logs wpml ${whereClause}`, + params, + ) + params.push(pageSize, (page - 1) * pageSize) const result = await query( ` SELECT wpml.*, wpr.rule_key, wpr.product_name FROM work_product_match_logs wpml LEFT JOIN work_product_rules wpr ON wpr.id = wpml.rule_id + ${whereClause} ORDER BY wpml.created_at DESC, wpml.id DESC - LIMIT $1 + LIMIT $${params.length - 1} OFFSET $${params.length} `, - [Math.min(500, Math.max(1, Math.floor(Number(limit) || 100)))], + params, ) - return result.rows + return { items: result.rows, total: Number(totalResult.rows[0]?.total || 0) } } diff --git a/apps/backend/src/services/worker-platform/admin-kuaishou-match-service.ts b/apps/backend/src/services/worker-platform/admin-kuaishou-match-service.ts index 523cb7c7..5294c2e2 100644 --- a/apps/backend/src/services/worker-platform/admin-kuaishou-match-service.ts +++ b/apps/backend/src/services/worker-platform/admin-kuaishou-match-service.ts @@ -15,7 +15,12 @@ import type { JsonObject } from '../../types/json.js' import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js' import { createHttpError } from '../../utils/http.js' import { nowIso } from '../../utils/time.js' -import { normalizePage, normalizePageSize, safeParseJson } from '../admin/admin-query-utils.js' +import { + normalizeDateString, + normalizePage, + normalizePageSize, + safeParseJson, +} from '../admin/admin-query-utils.js' import { mapWorkProductRule, normalizeBoolean, @@ -262,8 +267,17 @@ export async function testAdminKuaishouProductMatch(payload: JsonObject = {}) { } export async function listAdminWorkProductMatchLogs(payload: JsonObject = {}) { - const limit = Math.min(500, normalizePositiveInteger(payload.limit, 100)) - return { items: (await listWorkProductMatchLogs(limit)).map(mapWorkProductMatchLog) } + const page = normalizePage(payload.page) + const pageSize = Math.min(200, normalizePageSize(payload.pageSize)) + const { items, total } = await listWorkProductMatchLogs({ + page, + pageSize, + matchStatus: String(payload.matchStatus || payload.status || '').trim(), + keyword: String(payload.keyword || '').trim(), + createdFrom: normalizeDateString(payload.createdFrom), + createdTo: normalizeDateString(payload.createdTo), + }) + return { items: items.map(mapWorkProductMatchLog), pagination: { page, pageSize, total } } } function mapWorkProductRuleMapping(mapping: WorkProductRuleMappingRow) { diff --git a/apps/frontend/src/pages/admin/panels/ProductMatchPanel.tsx b/apps/frontend/src/pages/admin/panels/ProductMatchPanel.tsx index bf170e46..78fd72da 100644 --- a/apps/frontend/src/pages/admin/panels/ProductMatchPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/ProductMatchPanel.tsx @@ -1,4 +1,4 @@ -import { PlayCircleOutlined, ReloadOutlined } from '@ant-design/icons' +import { PlayCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons' import { useQuery, useQueryClient } from '@tanstack/react-query' import { App, @@ -20,6 +20,8 @@ import { useState } from 'react' import { useNavigate } from 'react-router' import JsonPreview from '@/components/admin/JsonPreview' +import { AdminRangePicker } from '@/components/admin/AdminDatePicker' +import dayjs, { ADMIN_DATE_FORMAT } from '@/lib/dayjs' import { fetchAdminKuaishouIndustryShops, fetchAdminKuaishouMatchSources, @@ -31,6 +33,7 @@ import { import type { KuaishouMatchSource, WorkProductMatchLog } from '@/types/worker-platform' import type { AdminKuaishouIndustryShopOption } from '@/types/admin' import { formatAdminDateTime } from '@/utils/admin-time' +import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination' export default function ProductMatchPanel() { const { message } = App.useApp() @@ -42,6 +45,11 @@ export default function ProductMatchPanel() { >(null) const [selectedLog, setSelectedLog] = useState(null) const [activeTab, setActiveTab] = useState('sources') + const [logStatus, setLogStatus] = useState('') + const [logKeywordInput, setLogKeywordInput] = useState('') + const [logKeyword, setLogKeyword] = useState('') + const [logCreatedRange, setLogCreatedRange] = useState<[string, string] | null>(null) + const [logPage, setLogPage] = useState(1) const shopsQuery = useQuery({ queryKey: ['admin-kuaishou-industry-shops', 'product-match'], @@ -52,8 +60,22 @@ export default function ProductMatchPanel() { queryFn: () => fetchAdminKuaishouMatchSources(200), }) const logsQuery = useQuery({ - queryKey: ['admin-worker-platform-product-match-logs'], - queryFn: () => fetchAdminWorkProductMatchLogs(100), + queryKey: [ + 'admin-worker-platform-product-match-logs', + logStatus, + logKeyword, + logCreatedRange, + logPage, + ], + queryFn: () => + fetchAdminWorkProductMatchLogs({ + matchStatus: logStatus || undefined, + keyword: logKeyword || undefined, + createdFrom: logCreatedRange?.[0], + createdTo: logCreatedRange?.[1], + page: logPage, + pageSize: ADMIN_DEFAULT_PAGE_SIZE, + }), }) const matchConfigQuery = useQuery({ queryKey: ['admin-worker-platform-product-match-config'], @@ -62,6 +84,7 @@ export default function ProductMatchPanel() { const sources = sourcesQuery.data?.data.items || [] const logs = logsQuery.data?.data.items || [] + const logsPagination = logsQuery.data?.data.pagination const shops = shopsQuery.data?.data.shops || [] const matchConfig = matchConfigQuery.data?.data const shopNameBySellerId = createShopNameBySellerId(shops) @@ -305,10 +328,62 @@ export default function ProductMatchPanel() { }, { key: 'logs', - label: `匹配日志 (${logs.length})`, + label: `匹配日志 (共 ${logsPagination?.total ?? 0} 条)`, children: (
- + + setLogKeywordInput(event.target.value)} + onPressEnter={() => { + setLogKeyword(logKeywordInput.trim()) + setLogPage(1) + }} + /> + { + const [start, end] = dates || [] + setLogCreatedRange( + start && end + ? [start.format(ADMIN_DATE_FORMAT), end.format(ADMIN_DATE_FORMAT)] + : null, + ) + setLogPage(1) + }} + /> + - 仅记录包含快手发码上下文的真实匹配 + 日志全量保留,可按结果、关键词与日期筛选 @@ -325,7 +400,12 @@ export default function ProductMatchPanel() { loading={logsQuery.isLoading} columns={logColumns} dataSource={logs} - pagination={{ pageSize: 20, showSizeChanger: false }} + pagination={buildAdminTablePagination({ + page: logPage, + pageSize: ADMIN_DEFAULT_PAGE_SIZE, + total: logsPagination?.total || 0, + onChange: setLogPage, + })} size="small" />
diff --git a/apps/frontend/src/services/admin/worker-platform.ts b/apps/frontend/src/services/admin/worker-platform.ts index 1c8027f3..946d50fd 100644 --- a/apps/frontend/src/services/admin/worker-platform.ts +++ b/apps/frontend/src/services/admin/worker-platform.ts @@ -332,12 +332,10 @@ export function testAdminKuaishouProductMatch(rawPayload: string) { }>('/api/v1/admin/worker-platform/product-match-test', { rawPayload }) } -export function fetchAdminWorkProductMatchLogs(limit = 100) { - return apiGet<{ items: WorkProductMatchLog[] }>( +export function fetchAdminWorkProductMatchLogs(params?: Record) { + return apiGet>( '/api/v1/admin/worker-platform/product-match-logs', - { - limit, - }, + params, ) }