像 EnergySage 一样构建太阳能安装商目录

如果你曾经搜索过"find solar installer near me",你可能会登陆 EnergySage。它是美国主导的太阳能市场,连接房主与经过审查的安装商,生成有竞争力的报价,并每周拉取数百万个数据点。美国能源部直接资助了它的开发。如果你正在读这篇文章,你可能想构建类似的东西。

在过去的几年里,我为能源、家庭服务和B2B垂直领域的客户构建过目录式市场和基于地理位置的服务平台。太阳能安装商目录背后的架构并不是火箭科学,但有许多决定可以决定项目的成败。让我为你详细讲解整个过程——从数据建模到搜索基础设施再到真正驱动有机流量的SEO策略。

目录

理解太阳能市场模式

在写一行代码之前,你需要理解像 EnergySage 和 solar.com 这样的平台作为商业运作的原理。它们是具有特定收入模式的双边市场:

  1. 房主输入他们的地址和电力信息来请求太阳能报价
  2. 安装商付费访问这些线索并按价格和质量竞争
  3. 平台通过安装商订阅、按线索收费或分享已成交交易获得收入

EnergySage 报告说他们的市场为房主节省了大约每瓦 $0.20–$0.40 比直接联系安装商——典型的 5 kW 住宅系统上是 $1,000–$2,000。这种节省来自竞争。当多个安装商竞争同一个项目时,价格会下降。这是你需要复制的核心价值主张。

2025年太阳能市场仅在美国就是一个价值 $30+ 亿的产业,住宅安装以每年约 15-20% 的速度增长。《通货膨胀削减法》的 30% 联邦税收抵免一直运行到 2032 年,所以这个市场不会放缓。

以下是主要参与者的样子:

平台 模式 收入来源 安装商网络
EnergySage 报价市场 线索费 + 数据许可 500+ 经过审查的安装商
Solar.com 引导市场 优惠定价佣金 策划的网络
Enphase 安装商定位器 设备绑定目录 硬件销售 认证的 Enphase 安装商
SolarReviews 评论目录 线索生成费 500+ 上市公司
Yelp/Google 普通目录 广告 未审查的上市

你的目录不需要与 EnergySage 直接竞争。利基机会存在:州特定目录、仅限商业的市场、电池存储专家或专注于特定设备品牌的目录。

核心架构决策

第一个真正的决定是你是在构建静态目录、动态市场还是介于两者之间的东西。每种都有完全不同的复杂性。

静态目录 vs. 动态市场

静态目录列出安装商的联系信息、评论和服务区域。可以想象是太阳能的黄页。复杂性低,快速构建,通过广告或特色列表变现。

动态市场处理整个报价到成交的工作流:线索捕获、安装商匹配、报价生成、比较工具,有时甚至融资。这就是 EnergySage 做的。复杂性高,但收入潜力更高。

大多数团队应该从目录开始,然后在上面分层市场功能。原因是:目录页面产生有机流量(想想成千上万个"[城市] 的太阳能安装商"页面),该流量为市场漏斗提供支持。

无头架构

对于这样的项目,我强烈推荐无头架构。以下是我会使用的设置:

  • 前端:Next.js 或 Astro 用于面向公众的网站
  • CMS:无头 CMS(Sanity、Contentful 或 Payload)用于安装商档案和编辑内容
  • API 层:自定义 Node.js/Express 或 tRPC API 用于搜索、报价和交易逻辑
  • 数据库:PostgreSQL 加 PostGIS 用于地理空间查询
  • 搜索:Algolia 或 Meilisearch 用于即时安装商搜索

我们为客户使用我们的 Next.js 开发无头 CMS 能力构建了类似的架构。无头方法让你独立地扩展内容层(数千个位置页面)与应用层(报价引擎、用户仪表板)。

单体仓库结构

我会使用 Turborepo 将其组织为单体仓库:

├── apps/
│   ├── web/              # Next.js 公开网站
│   ├── installer-portal/  # 安装商仪表板 (Next.js)
│   └── admin/            # 内部管理员 (React)
├── packages/
│   ├── database/         # Prisma 模式 + 迁移
│   ├── api/              # 共享 API 路由/tRPC
│   ├── ui/               # 共享组件库
│   └── geo/              # 地理空间实用程序
└── turbo.json

随着项目增长,这可以保持组织整洁。geo 包值得特别提及——你将在多个应用中重用地理空间逻辑(距离计算、服务区匹配、地理编码)。

安装商目录的数据库设计

这是大多数目录项目要么成功要么创建多年来困扰的地方。让我分享一个行之有效的模式。

核心实体

-- 安装商公司
CREATE TABLE installers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(255) NOT NULL,
  slug VARCHAR(255) UNIQUE NOT NULL,
  description TEXT,
  website_url VARCHAR(500),
  phone VARCHAR(20),
  email VARCHAR(255),
  logo_url VARCHAR(500),
  founded_year INTEGER,
  employee_count_range VARCHAR(50),
  license_number VARCHAR(100),
  is_verified BOOLEAN DEFAULT FALSE,
  verification_date TIMESTAMP,
  avg_rating DECIMAL(2,1) DEFAULT 0,
  total_reviews INTEGER DEFAULT 0,
  total_installations INTEGER DEFAULT 0,
  status VARCHAR(20) DEFAULT 'pending',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- 服务区域(使用 PostGIS 用于地理查询)
CREATE TABLE service_areas (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  area_name VARCHAR(255),
  geometry GEOMETRY(POLYGON, 4326),
  radius_miles INTEGER,
  center_point GEOMETRY(POINT, 4326),
  is_primary BOOLEAN DEFAULT FALSE
);

-- 认证和证书
CREATE TABLE installer_certifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  certification_type VARCHAR(100), -- 'NABCEP', 'Tesla Powerwall', 'Enphase', 等等
  certification_number VARCHAR(100),
  issued_date DATE,
  expiry_date DATE,
  verified BOOLEAN DEFAULT FALSE
);

-- 他们安装的设备/品牌
CREATE TABLE installer_equipment (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  equipment_type VARCHAR(50), -- 'panel', 'inverter', 'battery'
  brand VARCHAR(100),
  model VARCHAR(255),
  is_preferred BOOLEAN DEFAULT FALSE
);

-- 位置页面 (用于 SEO)
CREATE TABLE locations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  state VARCHAR(2) NOT NULL,
  city VARCHAR(255) NOT NULL,
  county VARCHAR(255),
  slug VARCHAR(255) UNIQUE NOT NULL,
  zip_codes TEXT[], -- 覆盖的邮编数组
  center_point GEOMETRY(POINT, 4326),
  avg_system_cost DECIMAL(10,2),
  avg_electricity_rate DECIMAL(5,4),
  avg_sun_hours DECIMAL(4,2),
  state_incentives JSONB,
  installer_count INTEGER DEFAULT 0,
  meta_title VARCHAR(70),
  meta_description VARCHAR(160),
  content TEXT, -- Markdown 编辑内容
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

服务区问题

这是棘手的部分。安装商不提供围绕单个点的整洁的圆形区域。他们提供奇形怪状的区域——可能是一个州的三个县,加上另一边的几个邮编。你有三个选择:

  1. 基于半径:安装商设置中心点和最大距离。简单但不准确。
  2. 邮编列表:安装商选择特定邮编。准确但对于大面积来说繁琐。
  3. 基于多边形:在地图上绘制实际的服务区边界。最准确,最好的用户体验,但需要 PostGIS 和地图绘制用户界面。

我会用选项 1 作为后备实现选项 3。在入职期间,让安装商要么在 Mapbox/Google Maps 界面上绘制他们的服务区域,要么只输入一个半径。使用 PostGIS 的 ST_ContainsST_DWithin 函数进行匹配:

-- 查找为特定点(房主地址)提供服务的安装商
SELECT i.* FROM installers i
JOIN service_areas sa ON sa.installer_id = i.id
WHERE ST_Contains(sa.geometry, ST_SetSRID(ST_MakePoint(-71.4128, 41.8240), 4326))
  AND i.status = 'active'
ORDER BY i.avg_rating DESC;

基于地理位置的搜索和位置服务

"find solar installer near me" 查询就是一切。它是主要用户意图、主要 SEO 目标和核心用户体验流。你需要正确处理这个。

地理编码管道

当房主输入他们的地址或邮编时,这是流程:

  1. 客户端:使用 Google Places API 或 Mapbox Geocoding 的自动完成
  2. 地理编码:将地址转换为经纬度坐标
  3. 查询:查找服务区包含该点的安装商
  4. 丰富:拉入本地电力公司数据、州激励、平均太阳辐照度
  5. 排名:按相关性排序(评级、距离、响应时间、验证状态)
// 示例:安装商搜索的 Next.js API 路由
import { db } from '@/packages/database';
import { geocode } from '@/packages/geo';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const zipCode = searchParams.get('zip');
  const lat = searchParams.get('lat');
  const lng = searchParams.get('lng');

  let coordinates = { lat: Number(lat), lng: Number(lng) };

  if (zipCode && (!lat || !lng)) {
    coordinates = await geocode(zipCode);
  }

  const installers = await db.$queryRaw`
    SELECT 
      i.*,
      ST_Distance(
        sa.center_point::geography,
        ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326)::geography
      ) / 1609.34 as distance_miles
    FROM installers i
    JOIN service_areas sa ON sa.installer_id = i.id
    WHERE (
      ST_Contains(sa.geometry, ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326))
      OR ST_DWithin(
        sa.center_point::geography,
        ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326)::geography,
        sa.radius_miles * 1609.34
      )
    )
    AND i.status = 'active'
    ORDER BY i.is_verified DESC, i.avg_rating DESC, distance_miles ASC
    LIMIT 20
  `;

  return Response.json({ installers, coordinates });
}

搜索基础设施

对于即时搜索(按名称、认证、设备品牌过滤),一旦你超过几千个安装商,PostgreSQL 单独会变得缓慢。在上面分层 Algolia 或 Meilisearch:

功能 Algolia Meilisearch PostgreSQL 全文搜索
延迟 <50ms <50ms 100-500ms
地理过滤 内置 内置 需要 PostGIS
分面搜索 优秀 良好 手动
容错
定价 (2025) $1/1K 请求 免费 (自托管) 免费
设置复杂性 中等

Algolia 在每 1,000 个搜索请求 $1 对大多数目录来说是合理的。如果预算紧张,在 $20/月 VPS 上自托管 Meilisearch。

构建报价请求系统

这是将目录与市场区分开来的东西。报价系统是你的收入引擎。

报价请求流程

  1. 房主输入地址 → 自动检测电力公司和费率
  2. 屋顶评估(来自 Google Solar API 或 Aurora Solar 的卫星图像)
  3. 基于使用情况和屋顶空间的系统大小推荐
  4. 与该区域 3-8 个合格安装商进行匹配
  5. 安装商在 48 小时内提交竞争性报价
  6. 房主在标准化仪表板上比较报价

Google Solar API(2023 年推出,现在广泛可用)是这里的一个巨大捷径。它为美国 320+ 百万栋建筑提供太阳能潜力数据,包括:

  • 屋顶分段分析
  • 年日照小时数
  • 估计能量产生
  • 最优面板放置
// Google Solar API - 建筑见解
const response = await fetch(
  `https://solar.googleapis.com/v1/buildingInsights:findClosest?` +
  `location.latitude=${lat}&location.longitude=${lng}` +
  `&requiredQuality=HIGH&key=${GOOGLE_SOLAR_API_KEY}`
);

const data = await response.json();
// data.solarPotential.maxArrayPanelsCount
// data.solarPotential.maxSunshineHoursPerYear
// data.solarPotential.financialAnalyses

这消除了初始报价阶段对手动现场调查的需要,大大减少了报价时间。

报价数据模型

每个报价需要捕获足够的细节以进行有意义的比较:

interface SolarQuote {
  id: string;
  requestId: string;
  installerId: string;
  systemSizeKw: number;
  panelBrand: string;
  panelModel: string;
  panelCount: number;
  inverterBrand: string;
  inverterType: 'string' | 'micro' | 'hybrid';
  batteryIncluded: boolean;
  batteryBrand?: string;
  batteryCapacityKwh?: number;
  grossCost: number;
  federalTaxCredit: number;
  stateIncentives: number;
  utilityRebates: number;
  netCost: number;
  estimatedAnnualProduction: number;
  warrantyYears: number;
  estimatedPaybackYears: number;
  financingOptions: FinancingOption[];
  validUntil: Date;
}

本地太阳能页面的SEO架构

这是大多数太阳能目录要么成功要么失败的地方。EnergySage 从程序化位置页面生成大量有机流量——他们有针对每个州每个城市的页面,结构为 /local-data/solar-companies/{state}/{county}/{city}/

URL 结构

这是我推荐的 URL 层次结构:

/solar-installers/                        # 国家中心
/solar-installers/rhode-island/           # 州页面
/solar-installers/rhode-island/providence/ # 城市页面
/installer/blue-sky-solar/                # 个别安装商档案
/solar-costs/rhode-island/                # 按州的成本数据

程序化页面生成

使用 Next.js App Router,你可以在构建时生成成千上万的位置页面:

// app/solar-installers/[state]/[city]/page.tsx
export async function generateStaticParams() {
  const locations = await db.location.findMany({
    select: { state: true, slug: true }
  });
  
  return locations.map((loc) => ({
    state: loc.state.toLowerCase().replace(/\s+/g, '-'),
    city: loc.slug,
  }));
}

export async function generateMetadata({ params }: Props) {
  const location = await getLocation(params.state, params.city);
  return {
    title: `${location.city}, ${location.state} 最佳太阳能安装商 (2025)`,
    description: `比较 ${location.city} 的 ${location.installerCount} 家经过审查的太阳能公司。平均系统成本:$${location.avgSystemCost}。立即获取免费报价。`,
  };
}

对于有 10,000+ 位置页面的网站,考虑使用 Astro 而不是 Next.js。Astro 的静态生成对于内容丰富的网站显著更快——我们看到对于大型程序化页面集的构建时间从 45 分钟下降到 5 分钟以下。

位置页面的内容策略

不要只生成空白的模板页面。每个位置页面需要独特、有用的内容:

  • 该地区的活跃安装商数量
  • 该城市的平均太阳能系统成本(从你的报价数据中拉取)
  • 本地电力公司费率和净计量政策
  • 州和本地激励(这些差异很大)
  • 平均太阳辐照度 / 日照小时数
  • 顶级评价的安装商,具有真实评论
  • 最近报价的比较表格

例如,EnergySage 的普罗维登斯、RI 页面列出了 8 个市场安装商,其中包括关于经验、认证和设备偏好的详细信息。这是你竞争的深度。

安装商验证和信任信号

信任是整个游戏。EnergySage 验证安装商有 2-3 年的经验和多个成功安装,然后他们才能加入。Solar.com 谈到"剔除三流企业"。你需要一个验证管道。

验证清单

  • 营业执照:通过 API 验证州承包商执照(许多州有公开查询 API)
  • 保险:要求通用责任保险证明($1M+)和工人赔偿
  • NABCEP 认证:太阳能安装商的金标准——根据 NABCEP 的数据库进行验证
  • 经营年限:与州业务登记交叉参考
  • 安装历史:请求已完成项目的投资组合
  • 背景检查:对于竞标住宅项目的公司
  • 评论:从 Google、Yelp、BBB 和你自己的平台汇总

信任用户界面元素

在安装商档案上突出显示验证状态:

<div className="flex items-center gap-2">
  <VerifiedBadge />
  <span className="text-sm text-green-700">
    已验证:许可证 #12345 • NABCEP 认证 • 
    投保至 $2M • {yearsInBusiness} 年经验
  </span>
</div>

在每个页面上包含"为什么你可以信任我们"部分——EnergySage 这样做并且有效。透明地解释你的审查过程。

技术栈推荐

以下是我在 2025 年实际会用来构建这个的东西:

技术 为什么
前端 Next.js 15 (App Router) SSR + ISR 用于动态页面,RSC 用于性能
样式 Tailwind CSS + shadcn/ui 快速迭代、可访问的组件
CMS Payload CMS (自托管) 开源、适合自定义集合
数据库 PostgreSQL 16 + PostGIS 地理空间查询、可靠性
ORM Prisma + 原始 SQL 用于地理 两全其美
搜索 Meilisearch (自托管) 免费、快速、地理感知
地图 Mapbox GL JS 大规模定价比 Google Maps 更好
地理编码 Mapbox Geocoding API 每 1,000 个请求 $0.75
托管 Vercel (前端) + Railway (API + DB) 轻松部署、合理的定价
认证 Clerk 或 NextAuth.js 房主和安装商的单独流程
监控 Sentry + Plausible Analytics 错误跟踪 + 隐私友好的分析
电子邮件 Resend 报价通知的交易电子邮件

处理约 10,000 月度用户的 MVP 的总基础设施成本:大约 $150-300/月。在规模上(100K+ 月度用户),预期 $800-2,000/月,不包括 API 成本。

如果你想正确构建这个,请查看我们的 定价直接联系。我们为类似架构的多千页面目录网站构建过。

性能基准和成本预估

这是现实的开发时间表样子:

阶段 范围 时间表 成本范围
MVP 目录 安装商列表、地理搜索、位置页面 8-12 周 $30K-60K
报价系统 线索捕获、安装商匹配、报价比较 6-8 周 $25K-45K
安装商门户 仪表板、线索管理、分析 4-6 周 $15K-30K
房主仪表板 已保存报价、消息、项目跟踪 4-6 周 $15K-25K
支付集成 安装商订阅、线索付款 2-3 周 $8K-15K
总计 MVP 市场 24-35 周 $93K-175K

这些是具有经验丰富的团队的高质量构建的现实数字。我看到机构报价超过 $250K 以获得相似的范围,我看到团队试图以 $15K 完成并最终得到在真实流量下瓦解的东西。

你应该达到的性能目标:

  • 位置页面加载:< 1.5s (LCP) -- 这些是你的 SEO 金牌页面
  • 搜索结果:从按键到结果 < 200ms
  • 地理查询:对于"[邮编] 附近的安装商" < 100ms
  • 核心网络生命体征:整个板面全绿
  • 构建时间:5,000+ 静态页面 < 10 分钟

常见问题

构建一个像 EnergySage 这样的太阳能安装商目录需要花多少钱? 一个基本的具有地理搜索和安装商档案的目录可以以 $30K-60K 构建。一个具有报价比较、安装商仪表板和支付处理的完整市场通常运行 $93K-175K,具体取决于功能范围。EnergySage 本身由能源部赠款资助,已融资超过 $1000 万风险投资,所以不要指望在紧张预算上复制他们的完整功能集。

我应该为太阳能市场使用什么技术栈? 我推荐 Next.js 15 用于前端(SSR 能力对 SEO 至关重要),PostgreSQL 加 PostGIS 用于地理空间查询,以及 Algolia 或 Meilisearch 用于即时搜索。对于 CMS 层,Payload CMS 或 Sanity 适用于管理安装商档案和编辑内容。在 Vercel 上为前端和 Railway 或 Render 为你的数据库和 API 托管。

像 EnergySage 这样的太阳能市场平台如何赚钱? EnergySage 使用一个线索生成模型,其中安装商付费访问合格房主线索。他们还向投资公司、学术研究人员和政府机构许可他们广泛的太阳能定价数据。Solar.com 采取略有不同的方法,使用优惠定价佣金。大多数目录网站从特色列表费开始(每个安装商每月 $50-500),并在规模上演变成按线索定价(每个合格线索 $20-100)。

我如何获取太阳能安装商数据来填充我的目录? 从爬取公开数据开始:州承包商执照数据库、NABCEP 的认证安装商目录和制造商定位器(如 Enphase 的安装商网络)。然后构建一个入职流程,安装商可以声称和增强他们的档案。早期,你需要手动联系安装商——向他们提供免费列表来为市场播种。冷启动问题是真实的;没有人想加入一个空市场。

SEO 对太阳能安装商目录有多重要? 这本质上是你的整个增长策略。针对"[城市] 的太阳能安装商"查询的程序化位置页面是 EnergySage 驱动其大部分流量的方式。你会希望为每个主要城市和州有独特的页面,每个都具有真实的本地数据(平均成本、激励信息、安装商数量)。没有强劲的有机搜索性能,你会在试图竞争时烧掉付费获取预算。

我应该为地图功能使用 Google Maps 还是 Mapbox? 在大多数情况下是 Mapbox。Google Maps 定价快速变得昂贵——Maps JavaScript API 在每月 $200 的免费信用后对每 1,000 次加载收费 $7。Mapbox 对每 1,000 地图加载收费 $0.60,每月 50,000 次免费加载。对于可能在数千个位置页面上呈现地图的目录,Mapbox 可以每月为你节省数千美元。但是,使用 Google 的 Solar API 来获取建筑见解数据——没有其他东西相近。

我应该如何处理安装商验证和审查? 构建一个多步骤验证管道:检查州承包商执照数据库(许多有公开 API)、验证 NABCEP 认证、要求保险证明并交叉参考业务登记记录。EnergySage 需要 2-3 年的经验和多个已完成的安装。自动化你能做的,但期望进行一些手动审查。突出显示验证徽章——它们直接影响房主转换率。

我可以使用 Google Solar API 来估计太阳能潜力吗? 是的,你应该。Google 的 Solar API 覆盖美国 320+ 百万栋建筑,并提供屋顶分段分析、年日照数据和估计的能量产生。它每 1,000 个请求成本 $10 用于建筑见解。当房主输入他们的地址时使用它来预先填充系统大小估计——这大大改进了报价请求体验并在安装商甚至参与之前设置现实期望。