/** * 创建 Contact(联系人)和 WebhookLog(系统事件日志)表 * * 用法: cd backend && npx tsx scripts/create-contact-webhooklog-schema.ts * * 幂等:可重复执行,已存在的字段自动跳过 */ import 'dotenv/config'; import Parse from '../src/db/parse-client.js'; async function testConnection(): Promise { try { const n = await new Parse.Query('_User').count({ useMasterKey: true }); console.log(`✅ Parse 连接正常 (_User: ${n} 条)\n`); } catch (err: any) { console.error(`❌ Parse 连接失败: ${err.message}`); process.exit(1); } } async function ensureClassExists(className: string): Promise { try { await new Parse.Schema(className).get({ useMasterKey: true }); } catch { // 类不存在,创建一条空记录来建表,然后立即删除 const tmp = new Parse.Object(className); tmp.set('_placeholder', true); await tmp.save(null, { useMasterKey: true }); await tmp.destroy({ useMasterKey: true }); console.log(` 📦 ${className} 表已创建`); } } /* ═══════════════════════════════════════════════ 1. Contact(联系人表) ═══════════════════════════════════════════════ */ async function createContactTable(): Promise { console.log('═══ 1/3 Contact(联系人表)═══'); await ensureClassExists('Contact'); const schema = new Parse.Schema('Contact'); const fields: Array<{ name: string; type: string }> = [ { name: 'userId', type: 'String' }, { name: 'guid', type: 'String' }, { name: 'nickname', type: 'String' }, { name: 'realName', type: 'String' }, { name: 'remark', type: 'String' }, { name: 'avatar', type: 'String' }, { name: 'sex', type: 'Number' }, { name: 'phone', type: 'String' }, { name: 'corpName', type: 'String' }, { name: 'corpFullName', type: 'String' }, { name: 'corpId', type: 'String' }, { name: 'position', type: 'String' }, { name: 'alias', type: 'String' }, { name: 'unionid', type: 'String' }, { name: 'addTime', type: 'Number' }, { name: 'contactType', type: 'Number' }, { name: 'partyId', type: 'String' }, { name: 'status', type: 'String' }, ]; for (const f of fields) { try { await schema.addField(f.name, f.type); console.log(` + ${f.name} (${f.type})`); } catch (err: any) { if (err.message?.includes('already')) { console.log(` ~ ${f.name} 已存在`); } else { console.warn(` ⚠ ${f.name} 添加失败: ${err.message}`); } } } // CLP 权限:仅 masterKey 可读写 try { await schema.setCLP({ find: { 'requiresAuthentication': true }, get: { 'requiresAuthentication': true }, create: { 'requiresAuthentication': true }, update: { 'requiresAuthentication': true }, delete: { 'requiresAuthentication': true }, }); } catch { /* CLP 设置失败不阻塞 */ } await schema.update({ useMasterKey: true }); console.log(' Contact 表更新完成\n'); } /* ═══════════════════════════════════════════════ 2. WebhookLog(系统事件日志表) ═══════════════════════════════════════════════ */ async function createWebhookLogTable(): Promise { console.log('═══ 2/3 WebhookLog(系统事件日志表)═══'); await ensureClassExists('WebhookLog'); const schema = new Parse.Schema('WebhookLog'); const fields: Array<{ name: string; type: string }> = [ { name: 'guid', type: 'String' }, { name: 'cmd', type: 'Number' }, { name: 'msgType', type: 'Number' }, { name: 'msgData', type: 'Object' }, { name: 'timestamp', type: 'Date' }, ]; for (const f of fields) { try { await schema.addField(f.name, f.type); console.log(` + ${f.name} (${f.type})`); } catch (err: any) { if (err.message?.includes('already')) { console.log(` ~ ${f.name} 已存在`); } else { console.warn(` ⚠ ${f.name} 添加失败: ${err.message}`); } } } // CLP 权限 try { await schema.setCLP({ find: { 'requiresAuthentication': true }, get: { 'requiresAuthentication': true }, create: { 'requiresAuthentication': true }, update: { 'requiresAuthentication': true }, delete: { 'requiresAuthentication': true }, }); } catch { /* CLP 设置失败不阻塞 */ } await schema.update({ useMasterKey: true }); console.log(' WebhookLog 表更新完成\n'); } /* ═══════════════════════════════════════════════ 3. GroupChat 补字段(ownerName) ═══════════════════════════════════════════════ */ async function ensureGroupChatOwnerName(): Promise { console.log('═══ 3/3 GroupChat 补 ownerName 字段 ═══'); const schema = new Parse.Schema('GroupChat'); try { await schema.addField('ownerName', 'String'); console.log(' + ownerName (String)'); } catch (err: any) { if (err.message?.includes('already')) { console.log(' ~ ownerName 已存在'); } else { console.warn(` ⚠ ownerName 添加失败: ${err.message}`); } } await schema.update({ useMasterKey: true }); console.log(' GroupChat 表更新完成\n'); } /* ═══════════════════════════════════════════════ Main ═══════════════════════════════════════════════ */ async function main(): Promise { console.log('=== Contact + WebhookLog 表创建 & GroupChat 字段补全 ==='); console.log(`Server: ${Parse.serverURL}`); console.log(`App ID: ${Parse.applicationId}\n`); await testConnection(); await createContactTable(); await createWebhookLogTable(); await ensureGroupChatOwnerName(); console.log('=== 完成 ===\n'); } main().catch((err) => { console.error('创建失败:', err.message); process.exit(1); });