seed-test-users.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /**
  2. * 种子测试用户数据脚本
  3. *
  4. * 1. 清理现有非总监用户
  5. * 2. 确保 Store/District 数据存在
  6. * 3. 创建 3 个已分配测试用户(使用 Pointer 关联)
  7. *
  8. * 用法: cd backend && npx tsx scripts/seed-test-users.ts
  9. */
  10. import 'dotenv/config';
  11. import Parse from '../src/db/parse-client.js';
  12. async function main(): Promise<void> {
  13. console.log('=== 种子测试用户数据 ===\n');
  14. console.log(`Server: ${Parse.serverURL}`);
  15. console.log(`App ID: ${Parse.applicationId}\n`);
  16. // 测试连接
  17. try {
  18. await new Parse.Query('_User').limit(1).find({ useMasterKey: true });
  19. console.log('✅ Parse 连接正常\n');
  20. } catch (err: any) {
  21. console.error('❌ Parse 连接失败:', err.message);
  22. process.exit(1);
  23. }
  24. // ── 1. 查看当前数据 ──
  25. console.log('--- 当前数据 ---');
  26. const existingUsers = await new Parse.Query('_User').find({ useMasterKey: true }) as any[];
  27. console.log(`_User: ${existingUsers.length} 个用户`);
  28. for (const u of existingUsers) {
  29. console.log(` - ${u.get('name')} (${u.get('phone')}) role=${u.get('role')} storeId=${u.get('storeId') || '无'} districtId=${u.get('districtId') || '无'}`);
  30. }
  31. const existingStores = await new Parse.Query('Store').find({ useMasterKey: true }) as any[];
  32. console.log(`\nStore: ${existingStores.length} 个门店`);
  33. for (const s of existingStores) {
  34. console.log(` - ${s.id}: ${s.get('name')}`);
  35. }
  36. const existingDistricts = await new Parse.Query('District').find({ useMasterKey: true }) as any[];
  37. console.log(`\nDistrict: ${existingDistricts.length} 个区域`);
  38. for (const d of existingDistricts) {
  39. console.log(` - ${d.id}: ${d.get('name')}`);
  40. }
  41. // ── 2. 清理非总监用户 ──
  42. console.log('\n--- 清理非总监用户 ---');
  43. for (const u of existingUsers) {
  44. const role = u.get('role');
  45. if (role !== 'director') {
  46. console.log(` 删除: ${u.get('name')} (${u.get('phone')})`);
  47. await u.destroy({ useMasterKey: true });
  48. }
  49. }
  50. // ── 3. 确保 _User 表有 store/district Pointer 列 ──
  51. console.log('\n--- 检查 _User Schema ---');
  52. const userSchema = new Parse.Schema('_User');
  53. try { await userSchema.get({ useMasterKey: true }); } catch { /* 首次 */ }
  54. try { await userSchema.addField('store', 'Pointer', { targetClass: 'Store' }); console.log(' ✅ store Pointer 列已添加'); } catch { console.log(' ✅ store 列已存在'); }
  55. try { await userSchema.addField('district', 'Pointer', { targetClass: 'District' }); console.log(' ✅ district Pointer 列已添加'); } catch { console.log(' ✅ district 列已存在'); }
  56. await userSchema.update({ useMasterKey: true });
  57. // ── 4. 确保 Store 和 District 存在 ──
  58. let storeId: string;
  59. let storeName: string;
  60. let districtId: string;
  61. let districtName: string;
  62. if (existingDistricts.length > 0) {
  63. districtId = existingDistricts[0].id;
  64. districtName = existingDistricts[0].get('name');
  65. console.log(`\n✅ 使用已有区域: ${districtName} (${districtId})`);
  66. } else {
  67. const d = new Parse.Object('District');
  68. d.set('name', '华东区');
  69. d.set('sortOrder', 1);
  70. await d.save(null, { useMasterKey: true });
  71. districtId = d.id;
  72. districtName = '华东区';
  73. console.log(`\n✅ 创建区域: ${districtName} (${districtId})`);
  74. }
  75. if (existingStores.length > 0) {
  76. storeId = existingStores[0].id;
  77. storeName = existingStores[0].get('name');
  78. console.log(`✅ 使用已有门店: ${storeName} (${storeId})`);
  79. // 确保门店关联了区域
  80. const store = existingStores[0];
  81. if (!store.get('district')) {
  82. store.set('district', Parse.Object.createWithoutData('District', districtId));
  83. await store.save(null, { useMasterKey: true });
  84. console.log(` 已更新门店区域关联`);
  85. }
  86. } else {
  87. const s = new Parse.Object('Store');
  88. s.set('name', '上海旗舰店');
  89. s.set('district', Parse.Object.createWithoutData('District', districtId));
  90. await s.save(null, { useMasterKey: true });
  91. storeId = s.id;
  92. storeName = '上海旗舰店';
  93. console.log(`✅ 创建门店: ${storeName} (${storeId})`);
  94. }
  95. // ── 5. 创建 3 个测试用户(使用 Pointer) ──
  96. console.log('\n--- 创建测试用户 ---');
  97. // 先获取完整的 Store/District 对象(用于构建有效 Pointer)
  98. const storeObj = await new Parse.Query('Store').get(storeId, { useMasterKey: true });
  99. const districtObj = await new Parse.Query('District').get(districtId, { useMasterKey: true });
  100. const testUsers = [
  101. {
  102. name: '张总监',
  103. phone: '13800000001',
  104. password: '123456',
  105. role: 'director',
  106. store: null as any,
  107. district: null as any,
  108. },
  109. {
  110. name: '王督导',
  111. phone: '13800000002',
  112. password: '123456',
  113. role: 'regional_supervisor',
  114. store: null as any,
  115. district: districtObj,
  116. },
  117. {
  118. name: '李店长',
  119. phone: '13800000003',
  120. password: '123456',
  121. role: 'store_manager',
  122. store: storeObj,
  123. district: districtObj,
  124. },
  125. ];
  126. for (const tu of testUsers) {
  127. const username = `shequn:${tu.phone}`;
  128. // 先删除已存在的同名用户(确保干净重建)
  129. const existingQ = new Parse.Query('_User');
  130. existingQ.equalTo('username', username);
  131. const existingUser = await existingQ.first({ useMasterKey: true });
  132. if (existingUser) {
  133. await existingUser.destroy({ useMasterKey: true });
  134. console.log(` 删除旧用户: ${tu.phone}`);
  135. }
  136. try {
  137. await Parse.User.signUp(username, tu.password, {
  138. phone: tu.phone,
  139. appSource: 'shequn',
  140. name: tu.name,
  141. role: tu.role,
  142. department: tu.role === 'director' ? '总部' : '运营部',
  143. position: tu.role === 'director' ? 'director' : 'general_staff',
  144. });
  145. } catch (err: any) {
  146. console.error(` ❌ 创建用户 ${tu.phone} 失败:`, err.message);
  147. throw err;
  148. }
  149. // signUp 后重新查询并设置 Pointer 字段
  150. const q = new Parse.Query('_User');
  151. q.equalTo('username', username);
  152. const user = await q.first({ useMasterKey: true });
  153. if (!user) {
  154. console.error(` ❌ 找不到用户 ${tu.phone}`);
  155. continue;
  156. }
  157. if (tu.store) user.set('store', tu.store);
  158. if (tu.district) user.set('district', tu.district);
  159. await user.save(null, { useMasterKey: true });
  160. console.log(` ✅ ${tu.name} (${tu.phone}) role=${tu.role} store=${tu.store ? storeName : '—'} district=${tu.district ? districtName : '—'}`);
  161. }
  162. // ── 6. 验证 ──
  163. console.log('\n--- 验证 ---');
  164. const q = new Parse.Query('_User');
  165. q.include('store');
  166. q.include('district');
  167. q.ascending('phone');
  168. const finalUsers = await q.find({ useMasterKey: true }) as any[];
  169. for (const u of finalUsers) {
  170. const store = u.get('store');
  171. const district = u.get('district');
  172. console.log(` ${u.get('name')} | ${u.get('phone')} | ${u.get('role')} | store=${store?.get?.('name') || '—'} | district=${district?.get?.('name') || '—'}`);
  173. }
  174. console.log('\n✅ 种子数据完成!');
  175. console.log('一键测试账号:');
  176. console.log(' 总监: 13800000001 / 123456');
  177. console.log(' 区域督导: 13800000002 / 123456');
  178. console.log(' 店长: 13800000003 / 123456');
  179. }
  180. main().catch((err) => {
  181. console.error('失败:', err.message, err.stack);
  182. process.exit(1);
  183. });