承接 14b595b0。以下 7 处仍在函数体内 `new Intl.DateTimeFormat(...)`, 单次构造约 0.5ms,按行/按条调用时会线性放大: - utils/finance-date-range.ts formatShanghaiDate - utils/http.ts (已在上一个提交修复) - scheduler/cost-finance-sync-job.ts today - finance/sales-facts-service.ts formatDateOnly - finance/order-refund-service.ts toChinaDate - admin/admin-finance-service.ts chinaDateParts - admin/admin-dashboard-service.ts resolveChinaTodayRange - scripts/sync-cost-finance.ts formatShanghaiDate 均为同选项的纯 hoist,输出逐字节不变:formatShanghaiDate 用 9 例跨时区/ 毫秒/闰日语料与旧实现比对一致;其余为相同 en-CA/en-US + Asia/Shanghai 选项的等价替换,formatToParts 的取值改为单次遍历、去掉 fromEntries/reduce。
92 lines
3.0 KiB
TypeScript
92 lines
3.0 KiB
TypeScript
import { query } from '../../db/client.js'
|
|
import { TASK_STATUS } from '../../domain/task-status.js'
|
|
import { nowIso } from '../../utils/time.js'
|
|
import type { JsonObject } from '../../types/json.js'
|
|
|
|
export async function getAdminDashboardSummary() {
|
|
const { todayStartIso, tomorrowStartIso } = resolveChinaTodayRange(nowIso())
|
|
const paidPendingClaimStatuses = [
|
|
TASK_STATUS.PAID,
|
|
TASK_STATUS.LINK_GENERATED,
|
|
TASK_STATUS.PENDING_BINDING_PREPARE,
|
|
TASK_STATUS.WAITING_BINDING,
|
|
]
|
|
const claimingStatuses = [TASK_STATUS.CLAIMED, TASK_STATUS.ROLE_CONFIRMED, TASK_STATUS.REDEEMING]
|
|
const abnormalStatuses = [TASK_STATUS.RETRY_PENDING, TASK_STATUS.MANUAL_REVIEW]
|
|
const result = await query(
|
|
`
|
|
SELECT
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM orders
|
|
WHERE created_at >= $1 AND created_at < $2
|
|
) AS today_orders,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($3::text[])
|
|
) AS paid_pending_claim,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($4::text[])
|
|
) AS claiming_tasks,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = $5
|
|
AND updated_at >= $1 AND updated_at < $2
|
|
) AS redeemed_today,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($6::text[])
|
|
) AS abnormal_tasks
|
|
`,
|
|
[
|
|
todayStartIso,
|
|
tomorrowStartIso,
|
|
paidPendingClaimStatuses,
|
|
claimingStatuses,
|
|
TASK_STATUS.REDEEMED,
|
|
abnormalStatuses,
|
|
],
|
|
)
|
|
const summary: JsonObject = result.rows[0] || {}
|
|
|
|
return {
|
|
todayOrders: Number(summary?.today_orders || 0),
|
|
paidPendingClaim: Number(summary?.paid_pending_claim || 0),
|
|
claimingTasks: Number(summary?.claiming_tasks || 0),
|
|
redeemedToday: Number(summary?.redeemed_today || 0),
|
|
abnormalTasks: Number(summary?.abnormal_tasks || 0),
|
|
}
|
|
}
|
|
|
|
/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */
|
|
const CHINA_DAY_PARTS_FORMATTER = new Intl.DateTimeFormat('en-US', {
|
|
timeZone: 'Asia/Shanghai',
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
})
|
|
|
|
/** 返回中国时区当天对应的 UTC 查询范围,便于使用时间索引。 */
|
|
function resolveChinaTodayRange(now: string) {
|
|
let year = 0
|
|
let month = 1
|
|
let day = 1
|
|
for (const part of CHINA_DAY_PARTS_FORMATTER.formatToParts(new Date(now))) {
|
|
if (part.type === 'year') year = Number(part.value)
|
|
else if (part.type === 'month') month = Number(part.value)
|
|
else if (part.type === 'day') day = Number(part.value)
|
|
}
|
|
const todayStart = Date.UTC(year || 0, (month || 1) - 1, day || 1, -8)
|
|
const tomorrowStart = todayStart + 24 * 60 * 60 * 1000
|
|
|
|
return {
|
|
todayStartIso: new Date(todayStart).toISOString(),
|
|
tomorrowStartIso: new Date(tomorrowStart).toISOString(),
|
|
}
|
|
}
|