| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /**
- * 完整归属关系回填脚本:
- * 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<void> {
- 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<string, { name: string; districtId: string }>();
- 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<string, string>(); // communityId → storeId
- const communityNameMap = new Map<string, string>(); // communityName → storeId
- const communityNameToIdMap = new Map<string, string>(); // 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);
- });
|