import { createRequire } from 'module'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import PocketBase from 'pocketbase'; const require = createRequire(import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); let runtimeConfig = {}; try { runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js'); } catch (_error) { runtimeConfig = {}; } function readEnvFile(filePath) { if (!fs.existsSync(filePath)) return {}; const content = fs.readFileSync(filePath, 'utf8'); const result = {}; for (const rawLine of content.split(/\r?\n/)) { const line = rawLine.trim(); if (!line || line.startsWith('#')) continue; const index = line.indexOf('='); if (index === -1) continue; const key = line.slice(0, index).trim(); const value = line.slice(index + 1).trim(); result[key] = value; } return result; } const backendEnv = readEnvFile(path.resolve(__dirname, '..', 'back-end', '.env')); const PB_URL = String( process.env.PB_URL || backendEnv.POCKETBASE_API_URL || runtimeConfig.POCKETBASE_API_URL || 'http://127.0.0.1:8090' ).replace(/\/+$/, ''); const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || ''; if (!AUTH_TOKEN) { console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行 tbl_order 自动编号迁移。'); process.exit(1); } const pb = new PocketBase(PB_URL); const ORDER_ID_PATTERN = 'ORDER-[0-9]{13}-[A-Za-z0-9]{6}'; const ORDER_NUMBER_PATTERN = '[0-9]{18}'; function normalizeFieldPayload(field, overrides) { const payload = Object.assign({}, field, overrides || {}); if (payload.type === 'number') { if (Object.prototype.hasOwnProperty.call(payload, 'onlyInt')) payload.onlyInt = !!payload.onlyInt; } if (payload.type === 'autodate') { payload.onCreate = typeof payload.onCreate === 'boolean' ? payload.onCreate : true; payload.onUpdate = typeof payload.onUpdate === 'boolean' ? payload.onUpdate : false; } if (payload.type === 'file') { payload.maxSelect = typeof payload.maxSelect === 'number' ? payload.maxSelect : 0; payload.maxSize = typeof payload.maxSize === 'number' ? payload.maxSize : 0; payload.mimeTypes = Array.isArray(payload.mimeTypes) ? payload.mimeTypes : null; } if (payload.type === 'relation') { payload.maxSelect = typeof payload.maxSelect === 'number' ? payload.maxSelect : 1; payload.cascadeDelete = typeof payload.cascadeDelete === 'boolean' ? payload.cascadeDelete : false; } return payload; } function buildCollectionPayload(collection, fields) { return { name: collection.name, type: collection.type, listRule: collection.listRule, viewRule: collection.viewRule, createRule: collection.createRule, updateRule: collection.updateRule, deleteRule: collection.deleteRule, fields, indexes: collection.indexes || [], }; } async function migrateOrderFields() { const collection = await pb.collections.getOne('tbl_order'); const fields = Array.isArray(collection.fields) ? collection.fields : []; const orderIdField = fields.find((field) => field && field.name === 'order_id'); const orderNumberField = fields.find((field) => field && field.name === 'order_number'); if (!orderIdField || !orderNumberField) { throw new Error('tbl_order 缺少 order_id 或 order_number 字段'); } const orderIdReady = orderIdField.type === 'text' && String(orderIdField.autogeneratePattern || '') === ORDER_ID_PATTERN; const orderNumberReady = orderNumberField.type === 'text' && String(orderNumberField.autogeneratePattern || '') === ORDER_NUMBER_PATTERN; if (orderIdReady && orderNumberReady) { return { action: 'skipped', order_id: { type: orderIdField.type, autogeneratePattern: String(orderIdField.autogeneratePattern || ''), }, order_number: { type: orderNumberField.type, }, }; } const nextFields = fields .filter((field) => field && field.name !== 'id') .map((field) => { if (field.name === 'order_id') { return normalizeFieldPayload(field, { type: 'text', required: true, autogeneratePattern: ORDER_ID_PATTERN, }); } if (field.name === 'order_number') { return normalizeFieldPayload(field, { type: 'text', required: true, autogeneratePattern: ORDER_NUMBER_PATTERN, pattern: '', }); } return normalizeFieldPayload(field); }); await pb.collections.update(collection.id, buildCollectionPayload(collection, nextFields)); return { action: 'updated', }; } async function verifyOrderFields() { const collection = await pb.collections.getOne('tbl_order'); const orderIdField = (collection.fields || []).find((field) => field && field.name === 'order_id'); const orderNumberField = (collection.fields || []).find((field) => field && field.name === 'order_number'); const result = { order_id: { type: orderIdField ? orderIdField.type : '', autogeneratePattern: String(orderIdField && orderIdField.autogeneratePattern || ''), ok: !!orderIdField && orderIdField.type === 'text' && String(orderIdField.autogeneratePattern || '') === ORDER_ID_PATTERN, }, order_number: { type: orderNumberField ? orderNumberField.type : '', autogeneratePattern: String(orderNumberField && orderNumberField.autogeneratePattern || ''), ok: !!orderNumberField && orderNumberField.type === 'text' && String(orderNumberField.autogeneratePattern || '') === ORDER_NUMBER_PATTERN, }, }; if (!result.order_id.ok || !result.order_number.ok) { throw new Error('tbl_order 自动编号规则校验失败: ' + JSON.stringify(result, null, 2)); } return result; } async function main() { try { console.log(`🔄 正在连接 PocketBase: ${PB_URL}`); pb.authStore.save(AUTH_TOKEN, null); console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。'); const migrated = await migrateOrderFields(); console.log('📝 迁移结果:'); console.log(JSON.stringify(migrated, null, 2)); const verified = await verifyOrderFields(); console.log('📝 校验结果:'); console.log(JSON.stringify(verified, null, 2)); console.log('🎉 tbl_order 自动编号规则已就绪。'); } catch (error) { console.error('❌ tbl_order 自动编号迁移失败:'); console.error('status:', error && error.status); console.error('message:', error && error.message); console.error('response:', error && error.response); console.error('originalError:', error && error.originalError); console.error('stack:', error && error.stack); process.exitCode = 1; } } main();