/** * 完整归属关系回填脚本: * 1. 给 GroupChat 分配 storeId + storeName + communityId + communityName * 2. 同时设置 Pointer 字段 (store → Store, community → Community) * 3. 给 Store 设置 Pointer 字段 (district → District) * 4. 给 Community 设置 Pointer 字段 (store → Store) * * 分配逻辑: * 1. 有 communityId → 从 Community 表查 storeId * 2. 有 communityName → 匹配 Community.name 查 storeId * 3. 兜底 → 随机分配到一个门店 * * 用法: npx tsx scripts/assign-store-ids.ts */ import 'dotenv/config'; import Parse from '../src/db/parse-client.js'; function makePointer(className: string, objectId: string): Parse.Object | null { if (!objectId) return null; const ptr = new (Parse.Object as any)(className); ptr.id = objectId; return ptr; } async function main(): Promise { console.log('[AssignStoreIds] 开始完整归属关系回填…\n'); // 1. 获取所有门店 const storeQuery = new Parse.Query('Store'); storeQuery.limit(500); const stores = await storeQuery.find({ useMasterKey: true }); console.log(` 门店总数: ${stores.length}`); if (stores.length === 0) { console.warn('[AssignStoreIds] 无门店数据,请先运行 seed-data.ts'); return; } // 门店 map: id → { name, districtId } const storeMap = new Map(); for (const s of stores) { storeMap.set(s.id, { name: (s.get('name') as string) || '', districtId: (s.get('districtId') as string) || '', }); } // 2. 获取所有小区(用于 community → store 映射) const communityQuery = new Parse.Query('Community'); communityQuery.limit(500); const communities = await communityQuery.find({ useMasterKey: true }); const communityStoreMap = new Map(); // communityId → storeId const communityNameMap = new Map(); // communityName → storeId const communityNameToIdMap = new Map(); // communityName → communityId for (const c of communities) { const sid = c.get('storeId') as string; const name = c.get('name') as string; if (sid) { communityStoreMap.set(c.id, sid); if (name) { communityNameMap.set(name, sid); communityNameToIdMap.set(name, c.id); } } } console.log(` 小区总数: ${communities.length}, 有 storeId 的: ${communityStoreMap.size}`); // 3. 处理 GroupChat 归属 const groupQuery = new Parse.Query('GroupChat'); groupQuery.limit(1000); const groups = await groupQuery.find({ useMasterKey: true }); let assigned = 0; let fromCommunity = 0; let fromName = 0; let fromRandom = 0; let pointerSet = 0; const toSave: Parse.Object[] = []; for (const g of groups) { const existing = g.get('storeId') as string; if (existing) continue; const communityId = g.get('communityId') as string; const communityName = g.get('communityName') as string; let storeId: string | null = null; let matchCommunityId: string | null = null; let matchCommunityName: string | null = null; // 策略 1: 通过 communityId 查 if (communityId && communityStoreMap.has(communityId)) { storeId = communityStoreMap.get(communityId)!; fromCommunity++; } // 策略 2: 通过 communityName 匹配 if (!storeId && communityName && communityNameMap.has(communityName)) { storeId = communityNameMap.get(communityName)!; matchCommunityId = communityNameToIdMap.get(communityName) || null; matchCommunityName = communityName; fromName++; } // 策略 3: 随机分配 if (!storeId) { const randomStore = stores[Math.floor(Math.random() * stores.length)]; storeId = randomStore.id; fromRandom++; } const storeInfo = storeMap.get(storeId!); const storeName = storeInfo?.name || ''; g.set('storeId', storeId!); g.set('storeName', storeName); // 如果通过 communityName 匹配到了 communityId,也设置上 if (matchCommunityId) { g.set('communityId', matchCommunityId); g.set('communityName', matchCommunityName || ''); } // 设置 Pointer 字段 const storePtr = makePointer('Store', storeId!); if (storePtr) { g.set('store', storePtr); pointerSet++; } if (matchCommunityId) { const commPtr = makePointer('Community', matchCommunityId); if (commPtr) { g.set('community', commPtr); pointerSet++; } } toSave.push(g); assigned++; } // 4. 批量保存 GroupChat if (toSave.length > 0) { for (let i = 0; i < toSave.length; i += 100) { await Parse.Object.saveAll(toSave.slice(i, i + 100), { useMasterKey: true }); } } console.log(`\n[AssignStoreIds] GroupChat 处理完成:`); console.log(` GroupChat 总数: ${groups.length}`); console.log(` 已分配 storeId: ${assigned}`); console.log(` - 通过 communityId: ${fromCommunity}`); console.log(` - 通过 communityName: ${fromName}`); console.log(` - 随机分配: ${fromRandom}`); console.log(` Pointer 字段设置: ${pointerSet}`); // 5. 给已有 String 值但缺 Pointer 的记录补 Pointer console.log('\n[AssignStoreIds] 补充缺失的 Pointer 字段…'); const allGroups = await groupQuery.find({ useMasterKey: true }); let storePtrAdded = 0; let commPtrAdded = 0; const ptrToSave: Parse.Object[] = []; for (const g of allGroups) { let changed = false; const sid = g.get('storeId') as string; const cid = g.get('communityId') as string; if (sid && !g.get('store')) { const ptr = makePointer('Store', sid); if (ptr) { g.set('store', ptr); storePtrAdded++; changed = true; } } if (cid && !g.get('community')) { const ptr = makePointer('Community', cid); if (ptr) { g.set('community', ptr); commPtrAdded++; changed = true; } } if (changed) ptrToSave.push(g); } if (ptrToSave.length > 0) { for (let i = 0; i < ptrToSave.length; i += 100) { await Parse.Object.saveAll(ptrToSave.slice(i, i + 100), { useMasterKey: true }); } } console.log(` store Pointer 补充: ${storePtrAdded}, community Pointer 补充: ${commPtrAdded}`); // 6. 处理 Store.district Pointer console.log('\n[AssignStoreIds] 为 Store 补充 district Pointer…'); const allStores = await storeQuery.find({ useMasterKey: true }); let districtPtrAdded = 0; const storeToSave: Parse.Object[] = []; for (const s of allStores) { const did = s.get('districtId') as string; if (did && !s.get('district')) { const ptr = makePointer('District', did); if (ptr) { s.set('district', ptr); districtPtrAdded++; storeToSave.push(s); } } } if (storeToSave.length > 0) { for (let i = 0; i < storeToSave.length; i += 100) { await Parse.Object.saveAll(storeToSave.slice(i, i + 100), { useMasterKey: true }); } } console.log(` district Pointer 补充: ${districtPtrAdded}`); // 7. 处理 Community.store Pointer console.log('\n[AssignStoreIds] 为 Community 补充 store Pointer…'); const allCommunities = await communityQuery.find({ useMasterKey: true }); let commStorePtrAdded = 0; const commToSave: Parse.Object[] = []; for (const c of allCommunities) { const sid = c.get('storeId') as string; if (sid && !c.get('store')) { const ptr = makePointer('Store', sid); if (ptr) { c.set('store', ptr); commStorePtrAdded++; commToSave.push(c); } } } if (commToSave.length > 0) { for (let i = 0; i < commToSave.length; i += 100) { await Parse.Object.saveAll(commToSave.slice(i, i + 100), { useMasterKey: true }); } } console.log(` store Pointer 补充: ${commStorePtrAdded}`); console.log('\n[AssignStoreIds] 全部完成!'); } main().catch((err) => { console.error('[AssignStoreIds] 失败:', err); process.exit(1); });