From fb420c2988f29b88acfca784d1006d890de698e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 15:36:36 +0800 Subject: [PATCH] =?UTF-8?q?task-63(ASIN=20=E6=95=B0=E6=8D=AE=E4=B8=AD?= =?UTF-8?q?=E5=BF=83):=20=E5=AE=9E=E7=8E=B0=E5=8E=BB=E9=87=8D=E6=B1=87?= =?UTF-8?q?=E6=80=BB=E6=97=A5=E6=9C=9F=E5=92=8C=E5=9B=BD=E5=AE=B6=E7=AD=9B?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 dedupe-total-filter.ts 纯局部筛选态:createDedupeTotalFilterState 默认空、 dedupeTotalFilterActive 判定任一激活、toDedupeListParams 过滤态+页码 → AsinListParams (空白/非法日期不下发),复用共享 asin-filter 类型。8 用例全过,459 单测 + build 绿。 --- .../src/pages/asin/dedupe-total-filter.ts | 47 ++++++++++++ admin-frontend-vue/tests/task-63.test.ts | 74 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 admin-frontend-vue/src/pages/asin/dedupe-total-filter.ts create mode 100644 admin-frontend-vue/tests/task-63.test.ts diff --git a/admin-frontend-vue/src/pages/asin/dedupe-total-filter.ts b/admin-frontend-vue/src/pages/asin/dedupe-total-filter.ts new file mode 100644 index 00000000..2e5583fe --- /dev/null +++ b/admin-frontend-vue/src/pages/asin/dedupe-total-filter.ts @@ -0,0 +1,47 @@ +/** 去重汇总页面局部筛选状态(任务 63):日期区间/国家/分组/关键字,纯逻辑复用共享筛选类型。 */ +import { validIsoDateOrUndefined, type AsinListParams } from './asin-filter.ts' + +export interface DedupeTotalFilterState { + keyword: string + username: string + /** ISO yyyy-MM-dd;空串表示不限。 */ + startDate: string + endDate: string + groupId: number | null + country: string +} + +export function createDedupeTotalFilterState(): DedupeTotalFilterState { + return { keyword: '', username: '', startDate: '', endDate: '', groupId: null, country: '' } +} + +/** 是否有任一激活筛选条件。 */ +export function dedupeTotalFilterActive(state: DedupeTotalFilterState): boolean { + if ((state.keyword || '').trim()) return true + if ((state.username || '').trim()) return true + if ((state.country || '').trim()) return true + if (typeof state.groupId === 'number' && state.groupId >= 1) return true + if (validIsoDateOrUndefined(state.startDate) || validIsoDateOrUndefined(state.endDate)) return true + return false +} + +/** 过滤态 + 页码 → 列表查询参数;空白/非法日期不下发。 */ +export function toDedupeListParams( + state: DedupeTotalFilterState, + page: number, + pageSize: number, +): AsinListParams { + const params: AsinListParams = { page, pageSize } + const keyword = (state.keyword || '').trim() + const username = (state.username || '').trim() + const country = (state.country || '').trim() + if (keyword) params.keyword = keyword + if (username) params.username = username + if (country) params.country = country + const startDate = validIsoDateOrUndefined(state.startDate) + const endDate = validIsoDateOrUndefined(state.endDate) + if (startDate) params.startDate = startDate + if (endDate) params.endDate = endDate + if (typeof state.groupId === 'number' && state.groupId >= 1) params.groupId = state.groupId + return params +} diff --git a/admin-frontend-vue/tests/task-63.test.ts b/admin-frontend-vue/tests/task-63.test.ts new file mode 100644 index 00000000..4a29963b --- /dev/null +++ b/admin-frontend-vue/tests/task-63.test.ts @@ -0,0 +1,74 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { + createDedupeTotalFilterState, + dedupeTotalFilterActive, + toDedupeListParams, +} from '../src/pages/asin/dedupe-total-filter.ts' +import { ASIN_PAGE_DEFAULT_SIZE } from '../src/pages/asin/asin-filter.ts' + +test('test_task_063_dedupe_filter_normal_primary_path', () => { + // 正常主路径:完整筛选(国家+日期区间)可判定激活并映射到列表参数。 + const state = { ...createDedupeTotalFilterState(), country: 'DE', startDate: '2026-01-01', endDate: '2026-01-31' } + assert.equal(dedupeTotalFilterActive(state), true) + const params = toDedupeListParams(state, 2, 20) + assert.equal(params.page, 2) + assert.equal(params.pageSize, 20) + assert.equal(params.country, 'DE') + assert.equal(params.startDate, '2026-01-01') + assert.equal(params.endDate, '2026-01-31') +}) + +test('test_task_063_dedupe_filter_normal_variant_input', () => { + // 正常变体:仅分组/仅用户名筛选亦激活。 + assert.equal(dedupeTotalFilterActive({ ...createDedupeTotalFilterState(), groupId: 3 }), true) + assert.equal(dedupeTotalFilterActive({ ...createDedupeTotalFilterState(), username: '张' }), true) +}) + +test('test_task_063_dedupe_filter_normal_repeated_operation_is_idempotent', () => { + // 正常重复:映射纯函数不改输入。 + const state = createDedupeTotalFilterState() + assert.deepEqual(toDedupeListParams(state, 1, 15), toDedupeListParams(state, 1, 15)) +}) + +test('test_task_063_dedupe_filter_boundary_empty_input', () => { + // 边界空值:默认过滤态不激活;映射到列表参数为空筛选、带默认分页。 + const state = createDedupeTotalFilterState() + assert.equal(dedupeTotalFilterActive(state), false) + const params = toDedupeListParams(state, 1, 15) + assert.equal('country' in params, false) + assert.equal('startDate' in params, false) +}) + +test('test_task_063_dedupe_filter_boundary_single_item', () => { + // 边界单元素:单国家筛选激活。 + const state = { ...createDedupeTotalFilterState(), country: 'UK' } + assert.equal(dedupeTotalFilterActive(state), true) + assert.equal(toDedupeListParams(state, 1, 15).country, 'UK') +}) + +test('test_task_063_dedupe_filter_boundary_limit_or_missing_field', () => { + // 边界上限/缺字段:空白关键字/国家不激活;默认页大小常量透传。 + const state = { ...createDedupeTotalFilterState(), keyword: ' ', country: ' ' } + assert.equal(dedupeTotalFilterActive(state), false) + const params = toDedupeListParams(state, 1, ASIN_PAGE_DEFAULT_SIZE) + assert.equal('keyword' in params, false) +}) + +test('test_task_063_dedupe_filter_invalid_input_rejected', () => { + // 异常输入:非法 ISO 日期在映射层回 undefined(共享筛选会再校验)。 + const state = { ...createDedupeTotalFilterState(), startDate: 'bad', endDate: '2026-02-01' } + const params = toDedupeListParams(state, 1, 15) + assert.equal(params.startDate, undefined) + assert.equal(params.endDate, '2026-02-01') +}) + +test('test_task_063_dedupe_filter_dependency_failure_returns_actionable_message', () => { + // 依赖失败/可操作:过滤状态纯逻辑、复用共享筛选类型,不带 http。 + const mod = readSource('src/pages/asin/dedupe-total-filter.ts') + assert.equal(/axios|http\.|vue/.test(mod), false, '过滤状态保持纯逻辑') + assert.match(mod, /asin-filter/) + assert.match(mod, /startDate/) + assert.match(mod, /country/) +})