import Parse from './parse-client.js'; import { checkParseDatabase } from './parse-health.service.js'; function isSchemaExistsError(msg: string): boolean { return ( msg.includes('already exists') || msg.includes('Class already exists') || msg.includes('exists, cannot update') || msg.includes('Field already exists') ); } async function ensureClassFields( className: string, fields: Record, ): Promise { let added = 0; for (const [name, type] of Object.entries(fields)) { try { const schema = new Parse.Schema(className); schema.addField(name, type); await schema.update({ useMasterKey: true }); added++; } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); if (!isSchemaExistsError(msg)) { try { const schema = new Parse.Schema(className); schema.addField(name, type); await schema.save({ useMasterKey: true }); added++; } catch (saveErr: unknown) { const saveMsg = saveErr instanceof Error ? saveErr.message : String(saveErr); if (!isSchemaExistsError(saveMsg)) { console.warn(`[Schema] ${className}.${name}:`, saveMsg); } } } } } console.log(`[Schema] ${className} — fields ensured (${Object.keys(fields).length} total, ${added} applied)`); } export async function ensureSchemas(): Promise { const parseStatus = await checkParseDatabase(); if (!parseStatus.connected) { console.warn('[Schema] Parse 不可用,跳过 Schema 检查:', parseStatus.error); return; } await ensureClassFields('GroupChat', { roomId: 'String', roomName: 'String', ownerId: 'String', ownerName: 'String', ownerRole: 'String', memberCount: 'Number', avatarUrl: 'String', status: 'String', guid: 'String', storeId: 'String', storeName: 'String', communityId: 'String', communityName: 'String', activityLevel: 'String', healthScore: 'Number', healthStatus: 'String', hasDocument: 'Boolean', documentPinned: 'Boolean', documentInNotice: 'Boolean', messageCountToday: 'Number', messageCountTotal: 'Number', memberChange24h: 'Number', lifecyclePhase: 'String', healthGrade: 'String', opsActivityScore: 'Number', customerActivityScore: 'Number', }); await ensureClassFields('Store', { code: 'String', name: 'String', region: 'String', status: 'String', }); await ensureClassFields('Community', { name: 'String', storeId: 'String', storeName: 'String', address: 'String', status: 'String', deliveryDate: 'String', lifecyclePhase: 'String', totalHouseholds: 'Number', avgPrice: 'Number', groupCount: 'Number', }); await ensureClassFields('GroupMember', { roomId: 'String', userId: 'String', nickname: 'String', status: 'String', joinedAt: 'Date', leftAt: 'Date', guid: 'String', }); await ensureClassFields('Message', { msgUniqueIdentifier: 'String', roomId: 'String', senderId: 'String', senderName: 'String', receiverId: 'String', content: 'String', atList: 'Array', msgType: 'Number', isGroupChat: 'Number', timestamp: 'Date', guid: 'String', seq: 'Number', }); await ensureClassFields('_User', { phone: 'String', appSource: 'String', name: 'String', email: 'String', role: 'String', storeId: 'String', storeName: 'String', wecomUserId: 'String', regionCode: 'String', department: 'String', position: 'String', }); await ensureClassFields('OrgDepartment', { partyId: 'String', name: 'String', parentPartyId: 'String', regionCode: 'String', storeId: 'String', orderIndex: 'Number', syncedAt: 'Date', }); await ensureClassFields('OrgMember', { userId: 'String', name: 'String', partyIds: 'Array', mobile: 'String', position: 'String', syncedAt: 'Date', }); await ensureClassFields('SystemMigration', { key: 'String', completedAt: 'Date', valueNumber: 'Number', }); }