furniture-3d-system-Wu/furniture-3d-system/scripts/sync-product-inventory-snapshot.ts
WuYongJie 0861ad9603 feat(supply-chain): 商品库存快照落库并改读快照
- 新增 ProductInventorySnapshot / ProductInventorySnapshotRun 模型与正式迁移(条码×实际仓库)
- 同步任务复用现有库存/云仓/档案读取,临时表+事务原子替换,advisory lock 防重叠,失败保留上一份快照
- 新增 15 分钟 systemd timer 安装脚本与同步脚本(身份校验、凭据走环境变量)
- 列表/导出接口改读快照并展示 syncedAt,保留全零行过滤与导出字段选择
2026-10-19 14:41:18 +08:00

70 lines
2.1 KiB
TypeScript

import { loadEnvConfig } from '@next/env'
loadEnvConfig(process.cwd())
const CONFIRM_TOKEN = 'sync-product-inventory-snapshot'
function arg(name: string) {
const prefix = `--${name}=`
return process.argv.slice(2).find((value) => value.startsWith(prefix))?.slice(prefix.length) ?? ''
}
function required(name: string) {
const value = process.env[name]?.trim()
if (!value) throw new Error(`${name} is required.`)
return value
}
function identity(url: string) {
const parsed = new URL(url)
return [
parsed.protocol.replace(':', ''),
decodeURIComponent(parsed.username || ''),
parsed.hostname,
parsed.port || '',
parsed.pathname.replace(/^\//, ''),
parsed.searchParams.get('schema') ?? '',
].join('|')
}
function sourceIdentity() {
return [
'sqlserver',
required('JST_REPORT_DB_USER'),
required('JST_REPORT_DB_HOST'),
process.env.JST_REPORT_DB_PORT?.trim() || '1433',
required('JST_REPORT_DB_NAME'),
process.env.JST_REPORT_DB_SCHEMA?.trim() || 'rpt',
].join('|')
}
async function main() {
if (!process.argv.includes('--apply') || arg('confirm') !== CONFIRM_TOKEN) {
throw new Error(`写入商品库存快照需要 --apply 和 --confirm=${CONFIRM_TOKEN}`)
}
const databaseUrl = required('DATABASE_URL')
if (arg('expected-database-identity') !== identity(databaseUrl)) {
throw new Error('expected database identity mismatch')
}
if (arg('expected-source-database-identity') !== sourceIdentity()) {
throw new Error('expected source identity mismatch')
}
const { prisma } = await import('../src/lib/prisma')
const { productInventorySnapshotService } = await import(
'../src/server/services/product-inventory-snapshot.service'
)
try {
console.log(JSON.stringify({ stage: 'start' }))
const result = await productInventorySnapshotService.sync()
console.log(JSON.stringify({ stage: 'completed', ...result }, null, 2))
} finally {
await prisma.$disconnect()
}
}
void main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error))
process.exitCode = 1
})