assign-store-ids.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /**
  2. * 完整归属关系回填脚本:
  3. * 1. 给 GroupChat 分配 storeId + storeName + communityId + communityName
  4. * 2. 同时设置 Pointer 字段 (store → Store, community → Community)
  5. * 3. 给 Store 设置 Pointer 字段 (district → District)
  6. * 4. 给 Community 设置 Pointer 字段 (store → Store)
  7. *
  8. * 分配逻辑:
  9. * 1. 有 communityId → 从 Community 表查 storeId
  10. * 2. 有 communityName → 匹配 Community.name 查 storeId
  11. * 3. 兜底 → 随机分配到一个门店
  12. *
  13. * 用法: npx tsx scripts/assign-store-ids.ts
  14. */
  15. import 'dotenv/config';
  16. import Parse from '../src/db/parse-client.js';
  17. function makePointer(className: string, objectId: string): Parse.Object | null {
  18. if (!objectId) return null;
  19. const ptr = new (Parse.Object as any)(className);
  20. ptr.id = objectId;
  21. return ptr;
  22. }
  23. async function main(): Promise<void> {
  24. console.log('[AssignStoreIds] 开始完整归属关系回填…\n');
  25. // 1. 获取所有门店
  26. const storeQuery = new Parse.Query('Store');
  27. storeQuery.limit(500);
  28. const stores = await storeQuery.find({ useMasterKey: true });
  29. console.log(` 门店总数: ${stores.length}`);
  30. if (stores.length === 0) {
  31. console.warn('[AssignStoreIds] 无门店数据,请先运行 seed-data.ts');
  32. return;
  33. }
  34. // 门店 map: id → { name, districtId }
  35. const storeMap = new Map<string, { name: string; districtId: string }>();
  36. for (const s of stores) {
  37. storeMap.set(s.id, {
  38. name: (s.get('name') as string) || '',
  39. districtId: (s.get('districtId') as string) || '',
  40. });
  41. }
  42. // 2. 获取所有小区(用于 community → store 映射)
  43. const communityQuery = new Parse.Query('Community');
  44. communityQuery.limit(500);
  45. const communities = await communityQuery.find({ useMasterKey: true });
  46. const communityStoreMap = new Map<string, string>(); // communityId → storeId
  47. const communityNameMap = new Map<string, string>(); // communityName → storeId
  48. const communityNameToIdMap = new Map<string, string>(); // communityName → communityId
  49. for (const c of communities) {
  50. const sid = c.get('storeId') as string;
  51. const name = c.get('name') as string;
  52. if (sid) {
  53. communityStoreMap.set(c.id, sid);
  54. if (name) {
  55. communityNameMap.set(name, sid);
  56. communityNameToIdMap.set(name, c.id);
  57. }
  58. }
  59. }
  60. console.log(` 小区总数: ${communities.length}, 有 storeId 的: ${communityStoreMap.size}`);
  61. // 3. 处理 GroupChat 归属
  62. const groupQuery = new Parse.Query('GroupChat');
  63. groupQuery.limit(1000);
  64. const groups = await groupQuery.find({ useMasterKey: true });
  65. let assigned = 0;
  66. let fromCommunity = 0;
  67. let fromName = 0;
  68. let fromRandom = 0;
  69. let pointerSet = 0;
  70. const toSave: Parse.Object[] = [];
  71. for (const g of groups) {
  72. const existing = g.get('storeId') as string;
  73. if (existing) continue;
  74. const communityId = g.get('communityId') as string;
  75. const communityName = g.get('communityName') as string;
  76. let storeId: string | null = null;
  77. let matchCommunityId: string | null = null;
  78. let matchCommunityName: string | null = null;
  79. // 策略 1: 通过 communityId 查
  80. if (communityId && communityStoreMap.has(communityId)) {
  81. storeId = communityStoreMap.get(communityId)!;
  82. fromCommunity++;
  83. }
  84. // 策略 2: 通过 communityName 匹配
  85. if (!storeId && communityName && communityNameMap.has(communityName)) {
  86. storeId = communityNameMap.get(communityName)!;
  87. matchCommunityId = communityNameToIdMap.get(communityName) || null;
  88. matchCommunityName = communityName;
  89. fromName++;
  90. }
  91. // 策略 3: 随机分配
  92. if (!storeId) {
  93. const randomStore = stores[Math.floor(Math.random() * stores.length)];
  94. storeId = randomStore.id;
  95. fromRandom++;
  96. }
  97. const storeInfo = storeMap.get(storeId!);
  98. const storeName = storeInfo?.name || '';
  99. g.set('storeId', storeId!);
  100. g.set('storeName', storeName);
  101. // 如果通过 communityName 匹配到了 communityId,也设置上
  102. if (matchCommunityId) {
  103. g.set('communityId', matchCommunityId);
  104. g.set('communityName', matchCommunityName || '');
  105. }
  106. // 设置 Pointer 字段
  107. const storePtr = makePointer('Store', storeId!);
  108. if (storePtr) {
  109. g.set('store', storePtr);
  110. pointerSet++;
  111. }
  112. if (matchCommunityId) {
  113. const commPtr = makePointer('Community', matchCommunityId);
  114. if (commPtr) {
  115. g.set('community', commPtr);
  116. pointerSet++;
  117. }
  118. }
  119. toSave.push(g);
  120. assigned++;
  121. }
  122. // 4. 批量保存 GroupChat
  123. if (toSave.length > 0) {
  124. for (let i = 0; i < toSave.length; i += 100) {
  125. await Parse.Object.saveAll(toSave.slice(i, i + 100), { useMasterKey: true });
  126. }
  127. }
  128. console.log(`\n[AssignStoreIds] GroupChat 处理完成:`);
  129. console.log(` GroupChat 总数: ${groups.length}`);
  130. console.log(` 已分配 storeId: ${assigned}`);
  131. console.log(` - 通过 communityId: ${fromCommunity}`);
  132. console.log(` - 通过 communityName: ${fromName}`);
  133. console.log(` - 随机分配: ${fromRandom}`);
  134. console.log(` Pointer 字段设置: ${pointerSet}`);
  135. // 5. 给已有 String 值但缺 Pointer 的记录补 Pointer
  136. console.log('\n[AssignStoreIds] 补充缺失的 Pointer 字段…');
  137. const allGroups = await groupQuery.find({ useMasterKey: true });
  138. let storePtrAdded = 0;
  139. let commPtrAdded = 0;
  140. const ptrToSave: Parse.Object[] = [];
  141. for (const g of allGroups) {
  142. let changed = false;
  143. const sid = g.get('storeId') as string;
  144. const cid = g.get('communityId') as string;
  145. if (sid && !g.get('store')) {
  146. const ptr = makePointer('Store', sid);
  147. if (ptr) {
  148. g.set('store', ptr);
  149. storePtrAdded++;
  150. changed = true;
  151. }
  152. }
  153. if (cid && !g.get('community')) {
  154. const ptr = makePointer('Community', cid);
  155. if (ptr) {
  156. g.set('community', ptr);
  157. commPtrAdded++;
  158. changed = true;
  159. }
  160. }
  161. if (changed) ptrToSave.push(g);
  162. }
  163. if (ptrToSave.length > 0) {
  164. for (let i = 0; i < ptrToSave.length; i += 100) {
  165. await Parse.Object.saveAll(ptrToSave.slice(i, i + 100), { useMasterKey: true });
  166. }
  167. }
  168. console.log(` store Pointer 补充: ${storePtrAdded}, community Pointer 补充: ${commPtrAdded}`);
  169. // 6. 处理 Store.district Pointer
  170. console.log('\n[AssignStoreIds] 为 Store 补充 district Pointer…');
  171. const allStores = await storeQuery.find({ useMasterKey: true });
  172. let districtPtrAdded = 0;
  173. const storeToSave: Parse.Object[] = [];
  174. for (const s of allStores) {
  175. const did = s.get('districtId') as string;
  176. if (did && !s.get('district')) {
  177. const ptr = makePointer('District', did);
  178. if (ptr) {
  179. s.set('district', ptr);
  180. districtPtrAdded++;
  181. storeToSave.push(s);
  182. }
  183. }
  184. }
  185. if (storeToSave.length > 0) {
  186. for (let i = 0; i < storeToSave.length; i += 100) {
  187. await Parse.Object.saveAll(storeToSave.slice(i, i + 100), { useMasterKey: true });
  188. }
  189. }
  190. console.log(` district Pointer 补充: ${districtPtrAdded}`);
  191. // 7. 处理 Community.store Pointer
  192. console.log('\n[AssignStoreIds] 为 Community 补充 store Pointer…');
  193. const allCommunities = await communityQuery.find({ useMasterKey: true });
  194. let commStorePtrAdded = 0;
  195. const commToSave: Parse.Object[] = [];
  196. for (const c of allCommunities) {
  197. const sid = c.get('storeId') as string;
  198. if (sid && !c.get('store')) {
  199. const ptr = makePointer('Store', sid);
  200. if (ptr) {
  201. c.set('store', ptr);
  202. commStorePtrAdded++;
  203. commToSave.push(c);
  204. }
  205. }
  206. }
  207. if (commToSave.length > 0) {
  208. for (let i = 0; i < commToSave.length; i += 100) {
  209. await Parse.Object.saveAll(commToSave.slice(i, i + 100), { useMasterKey: true });
  210. }
  211. }
  212. console.log(` store Pointer 补充: ${commStorePtrAdded}`);
  213. console.log('\n[AssignStoreIds] 全部完成!');
  214. }
  215. main().catch((err) => {
  216. console.error('[AssignStoreIds] 失败:', err);
  217. process.exit(1);
  218. });