task-117(任务与重复分析中心): 实现重复检查明细列表
新增 duplicate-filter.ts(明细筛选 snake 查询) 与 parseDuplicateItemsPage/ fetchDuplicateItems(GET /duplicate-check-items)。 TDD: task-117.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/** 店铺数据重复检查适配(任务 115-119):/api/admin/shop-data-crawl/duplicate-check-* 系列端点。 */
|
||||
import { http } from '@/api/http'
|
||||
import { parseDuplicateOverview, type DuplicateOverview } from './duplicate-model.ts'
|
||||
import { parseDuplicateItemsPage, parseDuplicateOverview, type DuplicateOverview } from './duplicate-model.ts'
|
||||
import { toDuplicateItemsQuery, type DuplicateFilter } from './duplicate-filter.ts'
|
||||
|
||||
export const DUPLICATE_CHECK_ENDPOINT = '/api/admin/shop-data-crawl'
|
||||
|
||||
@@ -11,3 +12,15 @@ export async function fetchDuplicateOverview(force = false): Promise<DuplicateOv
|
||||
})
|
||||
return parseDuplicateOverview(data)
|
||||
}
|
||||
|
||||
/** 加载撞款明细分页列表:GET /duplicate-check-items。 */
|
||||
export async function fetchDuplicateItems(
|
||||
filter: DuplicateFilter,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<ReturnType<typeof parseDuplicateItemsPage>> {
|
||||
const { data } = await http.get<unknown>(`${DUPLICATE_CHECK_ENDPOINT}/duplicate-check-items`, {
|
||||
params: toDuplicateItemsQuery(filter, page, pageSize),
|
||||
})
|
||||
return parseDuplicateItemsPage(data)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/** 重复检查明细筛选与分页(任务 117):与 /duplicate-check-items 的 snake 参数契约对齐;纯逻辑。 */
|
||||
|
||||
export const DUPLICATE_DEFAULT_PAGE_SIZE = 20
|
||||
export const DUPLICATE_MIN_PAGE_SIZE = 10
|
||||
export const DUPLICATE_MAX_PAGE_SIZE = 100
|
||||
export const DUPLICATE_PAGE_MIN = 1
|
||||
|
||||
export interface DuplicateFilter {
|
||||
view: string
|
||||
asin: string
|
||||
shopName: string
|
||||
country: string
|
||||
site: string
|
||||
dateFrom: string
|
||||
dateTo: string
|
||||
}
|
||||
|
||||
export interface DuplicateItemsQuery {
|
||||
page: number
|
||||
page_size: number
|
||||
view?: string
|
||||
asin?: string
|
||||
shop_name?: string
|
||||
country?: string
|
||||
site?: string
|
||||
date_from?: string
|
||||
date_to?: string
|
||||
}
|
||||
|
||||
function textOrUndefined(value: unknown): string | undefined {
|
||||
const text = typeof value === 'string' ? value.trim() : ''
|
||||
return text || undefined
|
||||
}
|
||||
|
||||
function finiteInt(value: unknown): number | null {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : null
|
||||
}
|
||||
|
||||
/** 前端筛选 + 页码 → Java 查询参数(snake,仅下发非空)。 */
|
||||
export function toDuplicateItemsQuery(filter: DuplicateFilter, page: unknown, pageSize: unknown): DuplicateItemsQuery {
|
||||
const safePage = Math.max(finiteInt(page) ?? DUPLICATE_PAGE_MIN, DUPLICATE_PAGE_MIN)
|
||||
const rawSize = finiteInt(pageSize)
|
||||
const pageSizeClamped =
|
||||
rawSize === null || rawSize < 1
|
||||
? DUPLICATE_DEFAULT_PAGE_SIZE
|
||||
: Math.min(Math.max(rawSize, DUPLICATE_MIN_PAGE_SIZE), DUPLICATE_MAX_PAGE_SIZE)
|
||||
const query: DuplicateItemsQuery = { page: safePage, page_size: pageSizeClamped }
|
||||
const view = textOrUndefined(filter.view)
|
||||
if (view) query.view = view
|
||||
const asin = textOrUndefined(filter.asin)
|
||||
if (asin) query.asin = asin
|
||||
const shopName = textOrUndefined(filter.shopName)
|
||||
if (shopName) query.shop_name = shopName
|
||||
const country = textOrUndefined(filter.country)
|
||||
if (country) query.country = country
|
||||
const site = textOrUndefined(filter.site)
|
||||
if (site) query.site = site
|
||||
const dateFrom = textOrUndefined(filter.dateFrom)
|
||||
if (dateFrom) query.date_from = dateFrom
|
||||
const dateTo = textOrUndefined(filter.dateTo)
|
||||
if (dateTo) query.date_to = dateTo
|
||||
return query
|
||||
}
|
||||
@@ -138,3 +138,31 @@ function parseDuplicateMetrics(raw: unknown): DuplicateMetrics | null {
|
||||
source: text(r.source),
|
||||
}
|
||||
}
|
||||
|
||||
/** 明细列表分页负载(data: {pending,items,total,page,page_size}) → 前端结果。 */
|
||||
export function parseDuplicateItemsPage(payload: unknown): {
|
||||
pending: boolean
|
||||
scannedAt: string
|
||||
items: DuplicateItem[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
} {
|
||||
const core = unwrap<unknown>(payload)
|
||||
const record = core && typeof core === 'object' ? (core as Record<string, unknown>) : {}
|
||||
const items = Array.isArray(record.items)
|
||||
? record.items.map((raw) => parseDuplicateItem(raw)).filter((item): item is DuplicateItem => item !== null)
|
||||
: []
|
||||
const total = count(record.total)
|
||||
const pageNumber = count(record.page) >= 1 ? count(record.page) : 1
|
||||
const rawSize = record.page_size ?? record.pageSize
|
||||
const pageSize = count(rawSize) >= 1 ? count(rawSize) : 20
|
||||
return {
|
||||
pending: record.pending === true,
|
||||
scannedAt: text(record.scanned_at ?? record.scannedAt),
|
||||
items,
|
||||
total,
|
||||
page: pageNumber,
|
||||
pageSize,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user