fix(admin): 订单列表默认近7天并按支付时间筛选

避免无日期过滤时展示全库,关键词搜索不受日期限制;SSE 插入裁剪并尊重筛选,新单入库优先使用平台支付时间。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-09-15 13:41:29 +08:00
co-authored by Cursor
parent b7d246e5b9
commit 40c51e1d8e
7 changed files with 279 additions and 58 deletions
@@ -15,8 +15,8 @@ test('订单列表综合筛选下推到总数和分页查询', async () => {
executorKey: 'affiliate_dash',
payStatus: 'paid',
taskStatus: 'manual_review',
dateFrom: '2026-09-01T00:00:00.000Z',
dateTo: '2026-09-02T23:59:59.999Z',
dateFrom: '2026-09-01',
dateTo: '2026-09-02',
})
assert.equal(result.total, 3)
@@ -24,25 +24,37 @@ test('订单列表综合筛选下推到总数和分页查询', async () => {
assert.match(calls[0]?.sql || '', /o\.platform_order_id.*ILIKE \$1/s)
assert.match(calls[0]?.sql || '', /oi\.sku_code.*oi\.sku_name.*ILIKE \$1/s)
assert.match(calls[0]?.sql || '', /kuaishouSendCode'.*itemTitle.*kuaishouSendCode'.*skuNick/s)
assert.match(calls[0]?.sql || '', /o\.provider = \$2/)
assert.match(calls[0]?.sql || '', /o\.pay_status = \$3/)
assert.match(calls[0]?.sql || '', /ft\.task_status = \$4/)
assert.match(calls[0]?.sql || '', /ft\.executor_key = \$5/)
assert.match(calls[0]?.sql || '', /o\.created_at >= \$6/)
assert.match(calls[0]?.sql || '', /o\.created_at <= \$7/)
assert.deepEqual(calls[0]?.params, [
'%皮肤店%',
'91kaquan',
'paid',
'manual_review',
'affiliate_dash',
'2026-09-01T00:00:00.000Z',
'2026-09-02T23:59:59.999Z',
])
assert.match(calls[1]?.sql || '', /LIMIT \$8 OFFSET \$9/)
assert.match(calls[0]?.sql || '', /o\.provider IN \('91kaquan', 'open_91', '91'\)/)
assert.match(calls[0]?.sql || '', /o\.pay_status = \$2/)
assert.match(calls[0]?.sql || '', /ft\.task_status = \$3/)
assert.match(calls[0]?.sql || '', /ft\.executor_key = \$4/)
// 有关键词时不应用日期过滤
assert.doesNotMatch(calls[0]?.sql || '', /paid_at/i)
assert.deepEqual(calls[0]?.params, ['%皮肤店%', 'paid', 'manual_review', 'affiliate_dash'])
assert.match(calls[1]?.sql || '', /LIMIT \$5 OFFSET \$6/)
assert.deepEqual(calls[1]?.params, [...(calls[0]?.params || []), 10, 10])
})
test('订单列表无关键词时按支付时间与中国时区日期筛选', async () => {
const calls: Array<{ sql: string; params: unknown[] }> = []
await listOrdersWithExecutor(createExecutor(calls, 0), {
dateFrom: '2026-09-01',
dateTo: '2026-09-07',
})
assert.match(
calls[0]?.sql || '',
/COALESCE\(o\.paid_at, o\.created_at\) >= \(\$1::date\)::timestamp AT TIME ZONE 'Asia\/Shanghai'/,
)
assert.match(
calls[0]?.sql || '',
/COALESCE\(o\.paid_at, o\.created_at\) < \(\(\$2::date \+ 1\)::timestamp AT TIME ZONE 'Asia\/Shanghai'\)/,
)
assert.deepEqual(calls[0]?.params, ['2026-09-01', '2026-09-07'])
assert.match(calls[1]?.sql || '', /ORDER BY COALESCE\(o\.paid_at, o\.created_at\) DESC/)
})
test('订单列表保留订单号和精确 SKU 旧筛选参数兼容', async () => {
const calls: Array<{ sql: string; params: unknown[] }> = []
+42 -9
View File
@@ -295,8 +295,7 @@ export async function listOrdersWithExecutor(
}
if (provider) {
params.push(provider)
filters.push(`o.provider = $${params.length}`)
appendProviderFilter(filters, params, provider)
}
if (platformOrderId) {
@@ -339,14 +338,20 @@ export async function listOrdersWithExecutor(
)
}
if (dateFrom) {
params.push(dateFrom)
filters.push(`o.created_at >= $${params.length}`)
// 有关键词时不限日期,避免默认近 N 天挡住搜单号/商品。
const applyDateFilter = Boolean(dateFrom || dateTo) && !String(keyword || '').trim()
if (applyDateFilter && dateFrom) {
params.push(String(dateFrom).trim().slice(0, 10))
filters.push(
`COALESCE(o.paid_at, o.created_at) >= ($${params.length}::date)::timestamp AT TIME ZONE 'Asia/Shanghai'`,
)
}
if (dateTo) {
params.push(dateTo)
filters.push(`o.created_at <= $${params.length}`)
if (applyDateFilter && dateTo) {
params.push(String(dateTo).trim().slice(0, 10))
filters.push(
`COALESCE(o.paid_at, o.created_at) < (($${params.length}::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')`,
)
}
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
@@ -362,7 +367,7 @@ export async function listOrdersWithExecutor(
SELECT o.*
FROM orders o
${whereClause}
ORDER BY o.id DESC
ORDER BY COALESCE(o.paid_at, o.created_at) DESC NULLS LAST, o.id DESC
LIMIT $${params.length - 1} OFFSET $${params.length}
`,
params,
@@ -373,3 +378,31 @@ export async function listOrdersWithExecutor(
total: Number(totalResult.rows[0]?.total || 0),
}
}
/** 来源渠道筛选:兼容历史 provider / platform 多种写法。 */
function appendProviderFilter(filters: string[], params: unknown[], provider: string) {
const normalized = String(provider || '').trim().toLowerCase()
if (!normalized) {
return
}
if (normalized === 'pdd') {
filters.push(`(o.provider = 'pdd' OR o.platform = 'pdd')`)
return
}
if (normalized === '91' || normalized === '91kaquan' || normalized === 'open_91') {
filters.push(
`(o.provider IN ('91kaquan', 'open_91', '91') OR o.platform IN ('91kaquan', '91'))`,
)
return
}
if (normalized === 'kuaishou' || normalized === 'kuaishou-industry') {
filters.push(`(o.provider LIKE 'kuaishou%' OR o.platform LIKE 'kuaishou%')`)
return
}
params.push(provider)
filters.push(`(o.provider = $${params.length} OR o.platform = $${params.length})`)
}
@@ -24,6 +24,7 @@ import { createHttpError } from '../../utils/http.js'
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import {
normalizeDateQuery,
normalizeDateString,
normalizePage,
normalizePageSize,
safeParseJson,
@@ -94,8 +95,8 @@ export async function getAdminOrders(
payStatus: String(query.payStatus || '').trim(),
taskStatus: String(query.taskStatus || '').trim(),
skuCode: String(query.skuCode || '').trim(),
dateFrom: normalizeDateQuery(query.dateFrom),
dateTo: normalizeDateQuery(query.dateTo, true),
dateFrom: normalizeDateString(query.dateFrom),
dateTo: normalizeDateString(query.dateTo),
})
const orderIds = items.map((item) => Number(item.id)).filter((id) => id > 0)
@@ -156,7 +156,8 @@ export async function upsertOrderFromSource(
: await createOrderWithExecutor(executor, {
...basePayload,
...mergedPayload,
createdAt: now,
// 优先用平台支付/成团时间,避免积压同步把历史单排成「刚刚入库」。
createdAt: String(event.paidAt || now).trim() || now,
updatedAt: now,
})