import process from 'node:process' import { closeDb } from '../src/db/client.js' import { syncActualWorkOrderCostSnapshots, syncEstimatedOrderItemCostSnapshots, } from '../src/services/cost-center/cost-snapshot-sync-service.js' import { rebuildSalesDailyFacts } from '../src/services/finance/sales-facts-service.js' import { resolveFinanceDateRange, shiftDate } from '../src/utils/finance-date-range.js' import { nowIso } from '../src/utils/time.js' type Mode = 'estimated' | 'actual' | 'facts' | 'all' const options = parseOptions(process.argv.slice(2)) const mode = normalizeMode(options.mode) const days = normalizeDays(options.days) const fallbackTo = options.to || formatShanghaiDate(new Date()) const fallbackFrom = options.from || shiftDate(fallbackTo, -(days - 1)) async function main() { const input = { dateFrom: fallbackFrom, dateTo: fallbackTo } const range = resolveFinanceDateRange(input, { defaultDays: days, maxDays: 400 }) const startedAt = Date.now() const result: Record = { mode, range } if (mode === 'estimated' || mode === 'all') { result.estimated = await syncEstimatedOrderItemCostSnapshots(input) } if (mode === 'actual' || mode === 'all') { result.actual = await syncActualWorkOrderCostSnapshots(input) } if (mode === 'facts' || mode === 'all') { result.facts = await rebuildSalesDailyFacts(input) } result.durationMs = Date.now() - startedAt result.finishedAt = nowIso() console.log(JSON.stringify(result, null, 2)) } main() .catch((error) => { console.error(error instanceof Error ? error.message : error) process.exitCode = 1 }) .finally(async () => { await closeDb() }) function parseOptions(args: string[]) { const result: Record = {} for (const arg of args) { if (!arg.startsWith('--')) continue const body = arg.slice(2) const index = body.indexOf('=') if (index === -1) { result[body] = 'true' } else { result[body.slice(0, index)] = body.slice(index + 1) } } return result } function normalizeMode(value: unknown): Mode { const mode = String(value || 'all').trim() if (mode === 'estimated' || mode === 'actual' || mode === 'facts' || mode === 'all') { return mode } throw new Error('--mode 只能是 estimated / actual / facts / all') } function normalizeDays(value: unknown) { const parsed = Number(value || 7) if (!Number.isInteger(parsed) || parsed < 1 || parsed > 400) { throw new Error('--days 必须是 1-400 的整数') } return parsed } function formatShanghaiDate(value: Date) { return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', }).format(value) }