create-contact-webhooklog-schema.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * 创建 Contact(联系人)和 WebhookLog(系统事件日志)表
  3. *
  4. * 用法: cd backend && npx tsx scripts/create-contact-webhooklog-schema.ts
  5. *
  6. * 幂等:可重复执行,已存在的字段自动跳过
  7. */
  8. import 'dotenv/config';
  9. import Parse from '../src/db/parse-client.js';
  10. async function testConnection(): Promise<void> {
  11. try {
  12. const n = await new Parse.Query('_User').count({ useMasterKey: true });
  13. console.log(`✅ Parse 连接正常 (_User: ${n} 条)\n`);
  14. } catch (err: any) {
  15. console.error(`❌ Parse 连接失败: ${err.message}`);
  16. process.exit(1);
  17. }
  18. }
  19. async function ensureClassExists(className: string): Promise<void> {
  20. try {
  21. await new Parse.Schema(className).get({ useMasterKey: true });
  22. } catch {
  23. // 类不存在,创建一条空记录来建表,然后立即删除
  24. const tmp = new Parse.Object(className);
  25. tmp.set('_placeholder', true);
  26. await tmp.save(null, { useMasterKey: true });
  27. await tmp.destroy({ useMasterKey: true });
  28. console.log(` 📦 ${className} 表已创建`);
  29. }
  30. }
  31. /* ═══════════════════════════════════════════════
  32. 1. Contact(联系人表)
  33. ═══════════════════════════════════════════════ */
  34. async function createContactTable(): Promise<void> {
  35. console.log('═══ 1/3 Contact(联系人表)═══');
  36. await ensureClassExists('Contact');
  37. const schema = new Parse.Schema('Contact');
  38. const fields: Array<{ name: string; type: string }> = [
  39. { name: 'userId', type: 'String' },
  40. { name: 'guid', type: 'String' },
  41. { name: 'nickname', type: 'String' },
  42. { name: 'realName', type: 'String' },
  43. { name: 'remark', type: 'String' },
  44. { name: 'avatar', type: 'String' },
  45. { name: 'sex', type: 'Number' },
  46. { name: 'phone', type: 'String' },
  47. { name: 'corpName', type: 'String' },
  48. { name: 'corpFullName', type: 'String' },
  49. { name: 'corpId', type: 'String' },
  50. { name: 'position', type: 'String' },
  51. { name: 'alias', type: 'String' },
  52. { name: 'unionid', type: 'String' },
  53. { name: 'addTime', type: 'Number' },
  54. { name: 'contactType', type: 'Number' },
  55. { name: 'partyId', type: 'String' },
  56. { name: 'status', type: 'String' },
  57. ];
  58. for (const f of fields) {
  59. try {
  60. await schema.addField(f.name, f.type);
  61. console.log(` + ${f.name} (${f.type})`);
  62. } catch (err: any) {
  63. if (err.message?.includes('already')) {
  64. console.log(` ~ ${f.name} 已存在`);
  65. } else {
  66. console.warn(` ⚠ ${f.name} 添加失败: ${err.message}`);
  67. }
  68. }
  69. }
  70. // CLP 权限:仅 masterKey 可读写
  71. try {
  72. await schema.setCLP({
  73. find: { 'requiresAuthentication': true },
  74. get: { 'requiresAuthentication': true },
  75. create: { 'requiresAuthentication': true },
  76. update: { 'requiresAuthentication': true },
  77. delete: { 'requiresAuthentication': true },
  78. });
  79. } catch { /* CLP 设置失败不阻塞 */ }
  80. await schema.update({ useMasterKey: true });
  81. console.log(' Contact 表更新完成\n');
  82. }
  83. /* ═══════════════════════════════════════════════
  84. 2. WebhookLog(系统事件日志表)
  85. ═══════════════════════════════════════════════ */
  86. async function createWebhookLogTable(): Promise<void> {
  87. console.log('═══ 2/3 WebhookLog(系统事件日志表)═══');
  88. await ensureClassExists('WebhookLog');
  89. const schema = new Parse.Schema('WebhookLog');
  90. const fields: Array<{ name: string; type: string }> = [
  91. { name: 'guid', type: 'String' },
  92. { name: 'cmd', type: 'Number' },
  93. { name: 'msgType', type: 'Number' },
  94. { name: 'msgData', type: 'Object' },
  95. { name: 'timestamp', type: 'Date' },
  96. ];
  97. for (const f of fields) {
  98. try {
  99. await schema.addField(f.name, f.type);
  100. console.log(` + ${f.name} (${f.type})`);
  101. } catch (err: any) {
  102. if (err.message?.includes('already')) {
  103. console.log(` ~ ${f.name} 已存在`);
  104. } else {
  105. console.warn(` ⚠ ${f.name} 添加失败: ${err.message}`);
  106. }
  107. }
  108. }
  109. // CLP 权限
  110. try {
  111. await schema.setCLP({
  112. find: { 'requiresAuthentication': true },
  113. get: { 'requiresAuthentication': true },
  114. create: { 'requiresAuthentication': true },
  115. update: { 'requiresAuthentication': true },
  116. delete: { 'requiresAuthentication': true },
  117. });
  118. } catch { /* CLP 设置失败不阻塞 */ }
  119. await schema.update({ useMasterKey: true });
  120. console.log(' WebhookLog 表更新完成\n');
  121. }
  122. /* ═══════════════════════════════════════════════
  123. 3. GroupChat 补字段(ownerName)
  124. ═══════════════════════════════════════════════ */
  125. async function ensureGroupChatOwnerName(): Promise<void> {
  126. console.log('═══ 3/3 GroupChat 补 ownerName 字段 ═══');
  127. const schema = new Parse.Schema('GroupChat');
  128. try {
  129. await schema.addField('ownerName', 'String');
  130. console.log(' + ownerName (String)');
  131. } catch (err: any) {
  132. if (err.message?.includes('already')) {
  133. console.log(' ~ ownerName 已存在');
  134. } else {
  135. console.warn(` ⚠ ownerName 添加失败: ${err.message}`);
  136. }
  137. }
  138. await schema.update({ useMasterKey: true });
  139. console.log(' GroupChat 表更新完成\n');
  140. }
  141. /* ═══════════════════════════════════════════════
  142. Main
  143. ═══════════════════════════════════════════════ */
  144. async function main(): Promise<void> {
  145. console.log('=== Contact + WebhookLog 表创建 & GroupChat 字段补全 ===');
  146. console.log(`Server: ${Parse.serverURL}`);
  147. console.log(`App ID: ${Parse.applicationId}\n`);
  148. await testConnection();
  149. await createContactTable();
  150. await createWebhookLogTable();
  151. await ensureGroupChatOwnerName();
  152. console.log('=== 完成 ===\n');
  153. }
  154. main().catch((err) => {
  155. console.error('创建失败:', err.message);
  156. process.exit(1);
  157. });