/** * 种子测试用户数据脚本 * * 1. 清理现有非总监用户 * 2. 确保 Store/District 数据存在 * 3. 创建 3 个已分配测试用户(使用 Pointer 关联) * * 用法: cd backend && npx tsx scripts/seed-test-users.ts */ import 'dotenv/config'; import Parse from '../src/db/parse-client.js'; async function main(): Promise { console.log('=== 种子测试用户数据 ===\n'); console.log(`Server: ${Parse.serverURL}`); console.log(`App ID: ${Parse.applicationId}\n`); // 测试连接 try { await new Parse.Query('_User').limit(1).find({ useMasterKey: true }); console.log('✅ Parse 连接正常\n'); } catch (err: any) { console.error('❌ Parse 连接失败:', err.message); process.exit(1); } // ── 1. 查看当前数据 ── console.log('--- 当前数据 ---'); const existingUsers = await new Parse.Query('_User').find({ useMasterKey: true }) as any[]; console.log(`_User: ${existingUsers.length} 个用户`); for (const u of existingUsers) { console.log(` - ${u.get('name')} (${u.get('phone')}) role=${u.get('role')} storeId=${u.get('storeId') || '无'} districtId=${u.get('districtId') || '无'}`); } const existingStores = await new Parse.Query('Store').find({ useMasterKey: true }) as any[]; console.log(`\nStore: ${existingStores.length} 个门店`); for (const s of existingStores) { console.log(` - ${s.id}: ${s.get('name')}`); } const existingDistricts = await new Parse.Query('District').find({ useMasterKey: true }) as any[]; console.log(`\nDistrict: ${existingDistricts.length} 个区域`); for (const d of existingDistricts) { console.log(` - ${d.id}: ${d.get('name')}`); } // ── 2. 清理非总监用户 ── console.log('\n--- 清理非总监用户 ---'); for (const u of existingUsers) { const role = u.get('role'); if (role !== 'director') { console.log(` 删除: ${u.get('name')} (${u.get('phone')})`); await u.destroy({ useMasterKey: true }); } } // ── 3. 确保 _User 表有 store/district Pointer 列 ── console.log('\n--- 检查 _User Schema ---'); const userSchema = new Parse.Schema('_User'); try { await userSchema.get({ useMasterKey: true }); } catch { /* 首次 */ } try { await userSchema.addField('store', 'Pointer', { targetClass: 'Store' }); console.log(' ✅ store Pointer 列已添加'); } catch { console.log(' ✅ store 列已存在'); } try { await userSchema.addField('district', 'Pointer', { targetClass: 'District' }); console.log(' ✅ district Pointer 列已添加'); } catch { console.log(' ✅ district 列已存在'); } await userSchema.update({ useMasterKey: true }); // ── 4. 确保 Store 和 District 存在 ── let storeId: string; let storeName: string; let districtId: string; let districtName: string; if (existingDistricts.length > 0) { districtId = existingDistricts[0].id; districtName = existingDistricts[0].get('name'); console.log(`\n✅ 使用已有区域: ${districtName} (${districtId})`); } else { const d = new Parse.Object('District'); d.set('name', '华东区'); d.set('sortOrder', 1); await d.save(null, { useMasterKey: true }); districtId = d.id; districtName = '华东区'; console.log(`\n✅ 创建区域: ${districtName} (${districtId})`); } if (existingStores.length > 0) { storeId = existingStores[0].id; storeName = existingStores[0].get('name'); console.log(`✅ 使用已有门店: ${storeName} (${storeId})`); // 确保门店关联了区域 const store = existingStores[0]; if (!store.get('district')) { store.set('district', Parse.Object.createWithoutData('District', districtId)); await store.save(null, { useMasterKey: true }); console.log(` 已更新门店区域关联`); } } else { const s = new Parse.Object('Store'); s.set('name', '上海旗舰店'); s.set('district', Parse.Object.createWithoutData('District', districtId)); await s.save(null, { useMasterKey: true }); storeId = s.id; storeName = '上海旗舰店'; console.log(`✅ 创建门店: ${storeName} (${storeId})`); } // ── 5. 创建 3 个测试用户(使用 Pointer) ── console.log('\n--- 创建测试用户 ---'); // 先获取完整的 Store/District 对象(用于构建有效 Pointer) const storeObj = await new Parse.Query('Store').get(storeId, { useMasterKey: true }); const districtObj = await new Parse.Query('District').get(districtId, { useMasterKey: true }); const testUsers = [ { name: '张总监', phone: '13800000001', password: '123456', role: 'director', store: null as any, district: null as any, }, { name: '王督导', phone: '13800000002', password: '123456', role: 'regional_supervisor', store: null as any, district: districtObj, }, { name: '李店长', phone: '13800000003', password: '123456', role: 'store_manager', store: storeObj, district: districtObj, }, ]; for (const tu of testUsers) { const username = `shequn:${tu.phone}`; // 先删除已存在的同名用户(确保干净重建) const existingQ = new Parse.Query('_User'); existingQ.equalTo('username', username); const existingUser = await existingQ.first({ useMasterKey: true }); if (existingUser) { await existingUser.destroy({ useMasterKey: true }); console.log(` 删除旧用户: ${tu.phone}`); } try { await Parse.User.signUp(username, tu.password, { phone: tu.phone, appSource: 'shequn', name: tu.name, role: tu.role, department: tu.role === 'director' ? '总部' : '运营部', position: tu.role === 'director' ? 'director' : 'general_staff', }); } catch (err: any) { console.error(` ❌ 创建用户 ${tu.phone} 失败:`, err.message); throw err; } // signUp 后重新查询并设置 Pointer 字段 const q = new Parse.Query('_User'); q.equalTo('username', username); const user = await q.first({ useMasterKey: true }); if (!user) { console.error(` ❌ 找不到用户 ${tu.phone}`); continue; } if (tu.store) user.set('store', tu.store); if (tu.district) user.set('district', tu.district); await user.save(null, { useMasterKey: true }); console.log(` ✅ ${tu.name} (${tu.phone}) role=${tu.role} store=${tu.store ? storeName : '—'} district=${tu.district ? districtName : '—'}`); } // ── 6. 验证 ── console.log('\n--- 验证 ---'); const q = new Parse.Query('_User'); q.include('store'); q.include('district'); q.ascending('phone'); const finalUsers = await q.find({ useMasterKey: true }) as any[]; for (const u of finalUsers) { const store = u.get('store'); const district = u.get('district'); console.log(` ${u.get('name')} | ${u.get('phone')} | ${u.get('role')} | store=${store?.get?.('name') || '—'} | district=${district?.get?.('name') || '—'}`); } console.log('\n✅ 种子数据完成!'); console.log('一键测试账号:'); console.log(' 总监: 13800000001 / 123456'); console.log(' 区域督导: 13800000002 / 123456'); console.log(' 店长: 13800000003 / 123456'); } main().catch((err) => { console.error('失败:', err.message, err.stack); process.exit(1); });