import { PrismaPg } from '@prisma/adapter-pg'; import { PrismaClient, UserRole, PropertyType, TransactionType, ListingStatus, Direction, } from '@prisma/client'; import pg from 'pg'; import { importMarketData } from '../scripts/import-market-data'; import { CITY_COORDINATES } from '../scripts/seed-districts'; import { seedPlans } from '../scripts/seed-plans'; const pool = new pg.Pool({ connectionString: process.env['DATABASE_URL'] }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); // ============================================================================= // Sample coordinates for HCM districts // ============================================================================= const _SAMPLE_LOCATIONS = CITY_COORDINATES['Hồ Chí Minh']; // ============================================================================= // Seed functions // ============================================================================= // seedPlans is imported from scripts/seed-plans.ts async function seedUsers() { console.log('Seeding sample users...'); const admin = await prisma.user.upsert({ where: { phone: '0900000001' }, update: {}, create: { phone: '0900000001', email: 'admin@goodgo.vn', fullName: 'Admin GoodGo', role: UserRole.ADMIN, kycStatus: 'VERIFIED', isActive: true, }, }); const agent1 = await prisma.user.upsert({ where: { phone: '0900000002' }, update: {}, create: { phone: '0900000002', email: 'agent.nguyen@goodgo.vn', fullName: 'Nguyễn Văn An', role: UserRole.AGENT, kycStatus: 'VERIFIED', isActive: true, }, }); const agent2 = await prisma.user.upsert({ where: { phone: '0900000003' }, update: {}, create: { phone: '0900000003', email: 'agent.tran@goodgo.vn', fullName: 'Trần Thị Bình', role: UserRole.AGENT, kycStatus: 'VERIFIED', isActive: true, }, }); const buyer = await prisma.user.upsert({ where: { phone: '0900000004' }, update: {}, create: { phone: '0900000004', email: 'buyer.le@gmail.com', fullName: 'Lê Minh Cường', role: UserRole.BUYER, kycStatus: 'NONE', isActive: true, }, }); const seller = await prisma.user.upsert({ where: { phone: '0900000005' }, update: {}, create: { phone: '0900000005', email: 'seller.pham@gmail.com', fullName: 'Phạm Đức Dũng', role: UserRole.SELLER, kycStatus: 'VERIFIED', isActive: true, }, }); // Create agent profiles await prisma.agent.upsert({ where: { userId: agent1.id }, update: {}, create: { userId: agent1.id, licenseNumber: 'BDS-2024-001', agency: 'GoodGo Premium Realty', qualityScore: 4.8, totalDeals: 127, responseTimeAvg: 15, bio: 'Chuyên viên bất động sản cao cấp Quận 1, Quận 7 với 10 năm kinh nghiệm.', serviceAreas: ['quan-1', 'quan-7', 'thu-duc'], isVerified: true, }, }); await prisma.agent.upsert({ where: { userId: agent2.id }, update: {}, create: { userId: agent2.id, licenseNumber: 'BDS-2024-002', agency: 'Saigon Homes', qualityScore: 4.5, totalDeals: 89, responseTimeAvg: 20, bio: 'Chuyên gia bất động sản Bình Thạnh, Phú Nhuận. Tư vấn miễn phí.', serviceAreas: ['binh-thanh', 'phu-nhuan', 'go-vap'], isVerified: true, }, }); console.log(' ✓ 5 users + 2 agent profiles seeded'); return { admin, agent1, agent2, buyer, seller }; } async function seedProperties(users: Awaited>) { console.log('Seeding sample properties and listings...'); const sampleProperties = [ { propertyType: PropertyType.APARTMENT, title: 'Căn hộ Vinhomes Central Park 3PN view sông', description: 'Căn hộ 3 phòng ngủ tại Vinhomes Central Park, tầng cao view sông Sài Gòn. Full nội thất cao cấp, tiện ích đầy đủ.', address: '208 Nguyễn Hữu Cảnh', ward: 'Phường 22', district: 'Quận Bình Thạnh', city: 'Hồ Chí Minh', lat: 10.7942, lng: 106.7214, areaM2: 108, usableAreaM2: 95, bedrooms: 3, bathrooms: 2, floor: 25, totalFloors: 50, direction: Direction.SOUTHEAST, yearBuilt: 2018, legalStatus: 'Sổ hồng', priceVND: BigInt(8_500_000_000), transactionType: TransactionType.SALE, projectName: 'Vinhomes Central Park', }, { propertyType: PropertyType.APARTMENT, title: 'Căn hộ The Sun Avenue 2PN cho thuê', description: 'Cho thuê căn hộ 2PN The Sun Avenue, nội thất đầy đủ, gần Metro, view đẹp.', address: '28 Mai Chí Thọ', ward: 'An Phú', district: 'Thủ Đức', city: 'Hồ Chí Minh', lat: 10.7696, lng: 106.7511, areaM2: 76, usableAreaM2: 68, bedrooms: 2, bathrooms: 2, floor: 15, totalFloors: 28, direction: Direction.NORTH, yearBuilt: 2020, legalStatus: 'Sổ hồng', priceVND: BigInt(15_000_000), transactionType: TransactionType.RENT, projectName: 'The Sun Avenue', }, { propertyType: PropertyType.TOWNHOUSE, title: 'Nhà phố Thảo Điền 1 trệt 3 lầu', description: 'Nhà phố khu compound an ninh Thảo Điền, 1 trệt 3 lầu, sân vườn rộng, gara ô tô.', address: '12 Nguyễn Văn Hưởng', ward: 'Thảo Điền', district: 'Thủ Đức', city: 'Hồ Chí Minh', lat: 10.8033, lng: 106.7391, areaM2: 200, usableAreaM2: 350, bedrooms: 4, bathrooms: 5, floors: 4, direction: Direction.SOUTH, yearBuilt: 2015, legalStatus: 'Sổ hồng', priceVND: BigInt(25_000_000_000), transactionType: TransactionType.SALE, projectName: null, }, { propertyType: PropertyType.LAND, title: 'Đất nền Quận 7 gần Phú Mỹ Hưng', description: 'Đất nền thổ cư 100%, sổ riêng, hẻm ô tô, gần trung tâm Phú Mỹ Hưng.', address: '56 Huỳnh Tấn Phát', ward: 'Phú Thuận', district: 'Quận 7', city: 'Hồ Chí Minh', lat: 10.7312, lng: 106.7283, areaM2: 120, usableAreaM2: null, bedrooms: null, bathrooms: null, direction: Direction.EAST, yearBuilt: null, legalStatus: 'Sổ đỏ', priceVND: BigInt(12_000_000_000), transactionType: TransactionType.SALE, projectName: null, }, { propertyType: PropertyType.OFFICE, title: 'Văn phòng cho thuê Quận 1 - 200m²', description: 'Văn phòng hạng B tại trung tâm Quận 1, full nội thất, hệ thống PCCC, thang máy.', address: '123 Nguyễn Huệ', ward: 'Bến Nghé', district: 'Quận 1', city: 'Hồ Chí Minh', lat: 10.7731, lng: 106.703, areaM2: 200, usableAreaM2: 180, bedrooms: null, bathrooms: 2, floor: 8, totalFloors: 15, direction: Direction.WEST, yearBuilt: 2010, legalStatus: 'Sổ hồng', priceVND: BigInt(80_000_000), transactionType: TransactionType.RENT, projectName: null, }, ]; const agents = await prisma.agent.findMany(); for (let i = 0; i < sampleProperties.length; i++) { const p = sampleProperties[i]!; const agent = agents[i % agents.length]! const _property = await prisma.$executeRaw` INSERT INTO "Property" ( "id", "propertyType", "title", "description", "address", "ward", "district", "city", "location", "areaM2", "usableAreaM2", "bedrooms", "bathrooms", "floors", "floor", "totalFloors", "direction", "yearBuilt", "legalStatus", "amenities", "nearbyPOIs", "metroDistanceM", "projectName", "createdAt", "updatedAt" ) VALUES ( ${`prop-${i + 1}`}, ${p.propertyType}::"PropertyType", ${p.title}, ${p.description}, ${p.address}, ${p.ward}, ${p.district}, ${p.city}, ST_SetSRID(ST_MakePoint(${p.lng}, ${p.lat}), 4326), ${p.areaM2}, ${p.usableAreaM2 ?? null}, ${p.bedrooms ?? null}, ${p.bathrooms ?? null}, ${p.floors ?? null}, ${p.floor ?? null}, ${p.totalFloors ?? null}, ${p.direction ?? null}::"Direction", ${p.yearBuilt ?? null}, ${p.legalStatus ?? null}, ${null}, ${null}, ${null}, ${p.projectName ?? null}, NOW(), NOW() ) ON CONFLICT ("id") DO NOTHING `; await prisma.listing.upsert({ where: { id: `listing-${i + 1}` }, update: {}, create: { id: `listing-${i + 1}`, propertyId: `prop-${i + 1}`, agentId: agent.id, sellerId: users.seller.id, transactionType: p.transactionType, status: ListingStatus.ACTIVE, priceVND: p.priceVND, pricePerM2: Number(p.priceVND) / p.areaM2, publishedAt: new Date(), }, }); } console.log(` ✓ ${sampleProperties.length} properties + listings seeded`); } // seedMarketIndex is now handled by importMarketData from scripts/import-market-data.ts // ============================================================================= // Main seed // ============================================================================= async function main() { console.log('🌱 Starting seed...\n'); await seedPlans(); const users = await seedUsers(); await seedProperties(users); await importMarketData(); console.log('\n✅ Seed completed successfully!'); } main() .catch((e) => { console.error('❌ Seed failed:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });