资金申请时间筛选支持精确到时分并修复截止日SQL括号缺失

This commit is contained in:
yml2213
2026-08-22 12:26:05 +08:00
parent 476ab166d7
commit f4e552c51c
6 changed files with 76 additions and 8 deletions
@@ -487,7 +487,7 @@ export type FinanceRequestListInput = {
status?: string
requestType?: string
keyword?: string
/** 申请时间区间(YYYY-MM-DD,上海时区,含起止日);后台列表筛选使用。 */
/** 申请时间区间(YYYY-MM-DD 含全天,或 YYYY-MM-DD HH:mm:ss 精确到秒;上海时区);后台列表筛选使用。 */
createdFrom?: string
createdTo?: string
/** 收款渠道筛选:alipay / wechat */
@@ -482,14 +482,18 @@ function buildWorkerFinanceRequestWhere({
}
if (createdFrom) {
params.push(createdFrom)
// 纯日期字符串 cast 后即当天 00:00:00;带时间则精确到秒
filters.push(
`wfr.created_at >= ($${params.length}::date)::timestamp AT TIME ZONE 'Asia/Shanghai'`,
`wfr.created_at >= ($${params.length}::timestamp) AT TIME ZONE 'Asia/Shanghai'`,
)
}
if (createdTo) {
params.push(createdTo)
// 带时间精确到秒(含截止时刻);纯日期保持含截止日全天
filters.push(
`wfr.created_at < (($${params.length}::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')`,
createdTo.length > 10
? `wfr.created_at <= ($${params.length}::timestamp) AT TIME ZONE 'Asia/Shanghai'`
: `wfr.created_at < (($${params.length}::date + 1)::timestamp) AT TIME ZONE 'Asia/Shanghai'`,
)
}
if (requestId) {
@@ -0,0 +1,27 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { normalizeDateTimeString } from './admin-query-utils.js'
test('normalizeDateTimeString 保留纯日期并按天过滤', () => {
assert.equal(normalizeDateTimeString('2026-08-22'), '2026-08-22')
})
test('normalizeDateTimeString 接受到分钟并补零秒', () => {
assert.equal(normalizeDateTimeString('2026-08-22 10:30'), '2026-08-22 10:30:00')
})
test('normalizeDateTimeString 接受到秒', () => {
assert.equal(normalizeDateTimeString('2026-08-22 10:30:59'), '2026-08-22 10:30:59')
})
test('normalizeDateTimeString 拒绝非法输入', () => {
assert.equal(normalizeDateTimeString(''), '')
assert.equal(normalizeDateTimeString(undefined), '')
assert.equal(normalizeDateTimeString('2026-8-22'), '')
assert.equal(normalizeDateTimeString('2026-08-22T10:30:00'), '')
assert.equal(normalizeDateTimeString('2026-08-22 24:00'), '')
assert.equal(normalizeDateTimeString('2026-08-22 10:60'), '')
assert.equal(normalizeDateTimeString('2026-08-22 10:30:60'), '')
assert.equal(normalizeDateTimeString('2026-02-31 10:30'), '')
})
@@ -32,6 +32,34 @@ export function normalizeDateString(rawValue: unknown) {
return /^\d{4}-\d{2}-\d{2}$/.test(normalized) ? normalized : ''
}
/**
* 校验并归一化日期时间参数:YYYY-MM-DD 或 YYYY-MM-DD HH:mm[:ss]。
* 纯日期原样返回(SQL 侧按当天零点处理),缺秒补 :00;非法或空值返回空字符串。
*/
export function normalizeDateTimeString(rawValue: unknown) {
const normalized = String(rawValue || '').trim()
if (!normalized) {
return ''
}
if (/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
return normalized
}
const match = normalized.match(/^(\d{4}-\d{2}-\d{2}) (\d{2}):(\d{2})(?::(\d{2}))?$/)
if (!match) {
return ''
}
const [, datePart, hour, minute, second] = match
// roundtrip 校验拦截 2026-02-31 这类会被 Date 溢出成其他日期的非法值
const parsedDate = new Date(`${datePart}T00:00:00Z`)
if (Number.isNaN(parsedDate.getTime()) || parsedDate.toISOString().slice(0, 10) !== datePart) {
return ''
}
if (Number(hour) > 23 || Number(minute) > 59 || Number(second ?? '0') > 59) {
return ''
}
return `${datePart} ${hour}:${minute}:${second ?? '00'}`
}
export function safeParseJson(rawText: unknown): JsonObject {
if (rawText && typeof rawText === 'object') {
return rawText
@@ -12,7 +12,7 @@ import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import {
normalizeDateString,
normalizeDateTimeString,
normalizePage,
normalizePageSize,
safeParseJson,
@@ -168,8 +168,8 @@ export async function listAdminWorkerFinanceRequests(query: JsonObject = {}) {
const status = normalizeFinanceRequestStatus(query.status)
const requestType = normalizeFinanceRequestType(query.requestType)
const keyword = String(query.keyword || '').trim()
const createdFrom = normalizeDateString(query.createdFrom)
const createdTo = normalizeDateString(query.createdTo)
const createdFrom = normalizeDateTimeString(query.createdFrom)
const createdTo = normalizeDateTimeString(query.createdTo)
const accountChannel = ['alipay', 'wechat'].includes(String(query.accountChannel || ''))
? String(query.accountChannel)
: ''