/** * 清除冗余 String 字段——外键关系统一使用 Pointer * * 清除的字段: * GroupChat: storeId, storeName, communityId, communityName * Store: districtId, districtName * Community: storeId, storeName, districtId, districtName * * 前置条件: assign-store-ids.ts 已运行,所有 Pointer 字段已就位 * 用法: npx tsx scripts/cleanup-redundant-fields.ts */ import 'dotenv/config'; import Parse from '../src/db/parse-client.js'; async function main(): Promise { console.log('[Cleanup] 开始清除冗余 String 字段…\n'); // 1. GroupChat: 移除 storeId, storeName, communityId, communityName console.log('[Cleanup] 处理 GroupChat…'); const gq = new Parse.Query('GroupChat'); gq.limit(1000); const groups = await gq.find({ useMasterKey: true }); let gCleaned = 0; const gToSave: Parse.Object[] = []; for (const g of groups) { const hasStorePtr = !!g.get('store'); const hasCommunityPtr = !!g.get('community'); const hasStoreId = g.get('storeId'); const hasStoreName = g.get('storeName'); const hasCommunityId = g.get('communityId'); const hasCommunityName = g.get('communityName'); if (!hasStorePtr && !hasCommunityPtr && !hasStoreId && !hasStoreName && !hasCommunityId && !hasCommunityName) { continue; } // 只在 Pointer 已设置时清除对应的 String 字段 if (hasStorePtr && (hasStoreId || hasStoreName)) { g.unset('storeId'); g.unset('storeName'); gCleaned++; } if (hasCommunityPtr && (hasCommunityId || hasCommunityName)) { g.unset('communityId'); g.unset('communityName'); gCleaned++; } gToSave.push(g); } if (gToSave.length > 0) { for (let i = 0; i < gToSave.length; i += 100) { await Parse.Object.saveAll(gToSave.slice(i, i + 100), { useMasterKey: true }); } } console.log(` GroupChat: ${groups.length} 条, 清除 ${gCleaned} 个冗余字段`); // 2. Store: 移除 districtId, districtName console.log('[Cleanup] 处理 Store…'); const sq = new Parse.Query('Store'); sq.limit(500); const stores = await sq.find({ useMasterKey: true }); let sCleaned = 0; const sToSave: Parse.Object[] = []; for (const s of stores) { const hasDistrictPtr = !!s.get('district'); const hasDistrictId = s.get('districtId'); if (hasDistrictPtr && hasDistrictId !== undefined) { s.unset('districtId'); s.unset('districtName'); sCleaned++; sToSave.push(s); } } if (sToSave.length > 0) { await Parse.Object.saveAll(sToSave, { useMasterKey: true }); } console.log(` Store: ${stores.length} 条, 清除 ${sCleaned} 个冗余字段`); // 3. Community: 移除 storeId, storeName, districtId, districtName console.log('[Cleanup] 处理 Community…'); const cq = new Parse.Query('Community'); cq.limit(500); const communities = await cq.find({ useMasterKey: true }); let cStoreCleaned = 0; let cDistrictCleaned = 0; const cToSave: Parse.Object[] = []; for (const c of communities) { let changed = false; const hasStorePtr = !!c.get('store'); if (hasStorePtr && c.get('storeId') !== undefined) { c.unset('storeId'); c.unset('storeName'); cStoreCleaned++; changed = true; } if (c.get('districtId') !== undefined || c.get('districtName') !== undefined) { c.unset('districtId'); c.unset('districtName'); cDistrictCleaned++; changed = true; } if (changed) cToSave.push(c); } if (cToSave.length > 0) { await Parse.Object.saveAll(cToSave, { useMasterKey: true }); } console.log(` Community: ${communities.length} 条, store 冗余 ${cStoreCleaned}, district 冗余 ${cDistrictCleaned}`); console.log('\n[Cleanup] 全部完成!'); } main().catch((err) => { console.error('[Cleanup] 失败:', err); process.exit(1); });