From a6ccb2e2d80c670aae5529f5990aec31b01117e7 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 30 Oct 2026 21:23:31 +0800 Subject: [PATCH] =?UTF-8?q?perf(finance):=20=E7=BB=9F=E4=B8=80=E6=8A=8A=20?= =?UTF-8?q?Intl.DateTimeFormat=20=E6=8F=90=E5=88=B0=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=BA=A7=EF=BC=8C=E6=B6=88=E9=99=A4=E9=80=90=E8=A1=8C=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=BC=80=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 承接 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。 --- apps/backend/scripts/sync-cost-finance.ts | 15 ++++++---- .../services/admin/admin-dashboard-service.ts | 30 ++++++++++--------- .../services/admin/admin-finance-service.ts | 26 +++++++++++----- .../services/finance/order-refund-service.ts | 15 ++++++---- .../services/finance/sales-facts-service.ts | 15 ++++++---- .../scheduler/cost-finance-sync-job.ts | 15 ++++++---- apps/backend/src/utils/finance-date-range.ts | 15 ++++++---- 7 files changed, 79 insertions(+), 52 deletions(-) diff --git a/apps/backend/scripts/sync-cost-finance.ts b/apps/backend/scripts/sync-cost-finance.ts index 8ef8043e..3f6d1ba8 100644 --- a/apps/backend/scripts/sync-cost-finance.ts +++ b/apps/backend/scripts/sync-cost-finance.ts @@ -78,11 +78,14 @@ function normalizeDays(value: unknown) { return parsed } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */ +const SHANGHAI_DATE_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + function formatShanghaiDate(value: Date) { - return new Intl.DateTimeFormat('en-CA', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(value) + return SHANGHAI_DATE_FORMATTER.format(value) } diff --git a/apps/backend/src/services/admin/admin-dashboard-service.ts b/apps/backend/src/services/admin/admin-dashboard-service.ts index 6aabea8d..210bd329 100644 --- a/apps/backend/src/services/admin/admin-dashboard-service.ts +++ b/apps/backend/src/services/admin/admin-dashboard-service.ts @@ -63,22 +63,24 @@ export async function getAdminDashboardSummary() { } } +/** 模块级只构造一次:`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) { - const parts = new Intl.DateTimeFormat('en-US', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }) - .formatToParts(new Date(now)) - .reduce>((result, part) => { - result[part.type] = part.value - return result - }, {}) - const year = Number(parts.year || 0) - const month = Number(parts.month || 1) - const day = Number(parts.day || 1) + 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 diff --git a/apps/backend/src/services/admin/admin-finance-service.ts b/apps/backend/src/services/admin/admin-finance-service.ts index 856d9465..aa1d3674 100644 --- a/apps/backend/src/services/admin/admin-finance-service.ts +++ b/apps/backend/src/services/admin/admin-finance-service.ts @@ -455,15 +455,25 @@ function parseFinanceBoundary(value: string, fallbackDate: string, end: boolean) : parsed.toISOString() } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */ +const CHINA_DATE_PARTS_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: CHINA_TIME_ZONE, + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + function chinaDateParts(value: string) { - const parts = new Intl.DateTimeFormat('en-CA', { - timeZone: CHINA_TIME_ZONE, - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).formatToParts(new Date(value)) - const result = Object.fromEntries(parts.map((part) => [part.type, part.value])) - return `${result.year}-${result.month}-${result.day}` + const parts = CHINA_DATE_PARTS_FORMATTER.formatToParts(new Date(value)) + let year = '' + let month = '' + let day = '' + for (const part of parts) { + if (part.type === 'year') year = part.value + else if (part.type === 'month') month = part.value + else if (part.type === 'day') day = part.value + } + return `${year}-${month}-${day}` } function chinaDateToUtcIso(value: string) { diff --git a/apps/backend/src/services/finance/order-refund-service.ts b/apps/backend/src/services/finance/order-refund-service.ts index 2390050e..16427be8 100644 --- a/apps/backend/src/services/finance/order-refund-service.ts +++ b/apps/backend/src/services/finance/order-refund-service.ts @@ -526,15 +526,18 @@ function normalizeTimestamp(value: unknown): string { return date.toISOString() } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */ +const CHINA_DATE_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + function toChinaDate(value: string): string { const date = new Date(value) if (Number.isNaN(date.getTime())) return '' - return new Intl.DateTimeFormat('en-CA', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(date) + return CHINA_DATE_FORMATTER.format(date) } function formatFen(value: number): string { diff --git a/apps/backend/src/services/finance/sales-facts-service.ts b/apps/backend/src/services/finance/sales-facts-service.ts index 92486785..f0954035 100644 --- a/apps/backend/src/services/finance/sales-facts-service.ts +++ b/apps/backend/src/services/finance/sales-facts-service.ts @@ -486,14 +486,17 @@ function mapSalesFact(row: SalesFactRow) { } } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐行调用会线性放大。 */ +const SHANGHAI_DATE_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + function formatDateOnly(value: string | Date): string { if (value instanceof Date) { - return new Intl.DateTimeFormat('en-CA', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(value) + return SHANGHAI_DATE_FORMATTER.format(value) } return String(value || '').slice(0, 10) } diff --git a/apps/backend/src/services/scheduler/cost-finance-sync-job.ts b/apps/backend/src/services/scheduler/cost-finance-sync-job.ts index 5e13d0e2..0a2cf65e 100644 --- a/apps/backend/src/services/scheduler/cost-finance-sync-job.ts +++ b/apps/backend/src/services/scheduler/cost-finance-sync-job.ts @@ -82,13 +82,16 @@ export async function runCostFinanceSyncJob(job: JsonObject = {}) { } } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */ +const SHANGHAI_DATE_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + function today() { - return new Intl.DateTimeFormat('en-CA', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(new Date()) + return SHANGHAI_DATE_FORMATTER.format(new Date()) } function shiftDays(days: number) { diff --git a/apps/backend/src/utils/finance-date-range.ts b/apps/backend/src/utils/finance-date-range.ts index 34d9bc55..16c45693 100644 --- a/apps/backend/src/utils/finance-date-range.ts +++ b/apps/backend/src/utils/finance-date-range.ts @@ -40,13 +40,16 @@ export function resolveFinanceDateRange( return { fromDate, toDate } } +/** 模块级只构造一次:`new Intl.DateTimeFormat()` 每次约 0.5ms,逐条调用会线性放大。 */ +const SHANGHAI_DATE_FORMATTER = new Intl.DateTimeFormat('en-CA', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + export function formatShanghaiDate(value: Date): string { - return new Intl.DateTimeFormat('en-CA', { - timeZone: 'Asia/Shanghai', - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(value) + return SHANGHAI_DATE_FORMATTER.format(value) } export function shiftDate(date: string, days: number): string {