资金申请时间筛选支持精确到时分并修复截止日SQL括号缺失
This commit is contained in:
@@ -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)
|
||||
: ''
|
||||
|
||||
Reference in New Issue
Block a user