跳转至
D3 · Deploy · 07web M6.1

07_web · M6.1 最小可发布部署方案

Astro 6 · @astrojs/node · SQLite · 邮件+企微选项矩阵 · v1.0 对比版
3
待拍板项(B3/B6/B7)
8
实现步骤
1
本地闭环目标

07_web · M6.1 最小可发布部署方案

摘要

本方案定义 Xisound 官网(07_web/xisound-website)M6.1 阶段最小可发布部署方案:本地 npm run dev 跑通完整留资闭环(表单提交 → SQLite 存储 → 邮件通知 / 企微推送)。 本文档不替用户拍板,而是对 3 项待定决策(B3 邮件服务 / B6 轻量 CRM / B7 企微 webhook)以及 2 项凭据(P1/P2)列出作用说明 + 每种做法的优劣对比,供用户后续决定。 用户已拍板项(直接写入):adapter @astrojs/node (standalone) · 数据库 SQLite · 账号/签名URL/部署 本轮不做。

本文档与既有规划的关系

  • AlgoDepartment/07_web/CLAUDE.md 已规划 M6 全阶段(本地 + 阿里云 ECS + Vercel 海外)· 本文档聚焦 M6.1 子阶段(仅本地)
  • AlgoDepartment/07_web/doc/M6-deployment-guide.md 已有 M6 完整部署指南 · 本文档为其子章节的拍板依据
  • 本文档落地在 D3 架构级 · 补充 07_web/xisound-website 的实现细节将在 D4 · web-astro

1. 现状与目标

1.1 当前状态(2026-05-06 探索结果)

维度 现状
框架 Astro 6.2.1 + Tailwind v4(@theme CSS) + TypeScript Strict
Node >=22.12.0(已在 package.json engines 声明)
包管理器 npm + 淘宝镜像 https://registry.npmmirror.com
输出 output: 'static'(纯静态 · 35 页 build)
Adapter 未安装astro.config.mjs 注释预留 @astrojs/node mode: standalone)
API endpoint src/pages/api/lead.ts 是 stub(prerender = true) · 仅占位
DB 无 · 无 better-sqlite3 依赖
邮件 / 企微 无代码 · 无凭据
.env.example 不存在 · 需新建
data/ 目录 不存在 · 需新建(放 leads.db
M5.8.1 ✅ 已完成 · 0 client:* 指令 · 静态 build 可用

1.2 M6.1 目标

一句话目标

本地 npm run dev(端口 4321)能打开官网、在留资表单提交一条线索,三个副作用必须都跑通: 1. 写入 ./data/leads.db(SQLite) 2. 发送邮件到运营邮箱(若 B3 选了 M1-M3) 3. 推送消息到企微机器人(若 B7 选了 W1)

任一项因凭据缺失(P1/P2)→ 降级为 console.log 打印,但代码路径必须完整(不能跳过)。

1.3 不在 M6.1 范围内的项(M6.2-M6.4 延后)

延后项 延后到
账号系统(登录 / 注册 / session) M6.2
下载签名 URL(白皮书 / SDK 等受保护资源) M6.3
生产部署(阿里云 ECS / Vercel 海外 / Nginx / ICP) M6.4
PostgreSQL 升级 M6.4
Sitemap 动态化 / SSR ISR M6.5+

2. 用户已拍板项(直接写入 M6.1)

决策 选择 含义
adapter @astrojs/node standalone 最小心智负担 · 自带 HTTP server · 无需 Nginx · 部署即 node ./dist/server/entry.mjs
output 模式 'hybrid' 大部分页面仍 SSG 预渲染 · 仅 /api/lead 等 endpoint 走 SSR
数据库 ✅ SQLite(文件 ./data/leads.db 零运维 · 文件即数据库 · M6.4 再升 PostgreSQL
数据库 driver better-sqlite3(推荐) 同步 API · 性能最佳 · Node 原生扩展 · 无需 ORM 开销
账号系统 ❌ 本轮不做 延后 M6.2
下载签名 URL ❌ 本轮不做 延后 M6.3
生产部署 ❌ 本轮不做 延后 M6.4

3. 【B3 · 邮件服务】作用说明 + 4 选项优劣对比

3.1 作用说明

邮件服务的作用:收到客户留资后,自动给运营邮箱发一封通知邮件,包含表单字段(姓名 / 公司 / 邮箱 / 电话 / 留言 / 来源页)。运营同事无需登录后台即可在手机收件箱看到新线索。

在 M6.1 里的位置:本地 npm run dev 时,表单提交 → SQLite 写入成功后 → 异步触发邮件发送(不阻塞响应)。

为什么不是必选:M6.1 最小闭环可以只存 SQLite + 企微推送(跳过邮件),后续再补。但推荐至少选一个,因为企微群推送对于不在群里的管理层不可见。

3.2 4 选项优劣对比

维度 M1 · Resend M2 · 阿里云 DirectMail M3 · SMTP(nodemailer) M4 · 暂不发邮件
适用地区 海外首选 · 国内可用但延迟 国内首选 · 阿里云生态 · 海外慢 全球通用 · 延迟取决于 SMTP 服务商
免费额度 3000 封/月 200 封/日(≈ 6000 封/月) 看 SMTP 提供方(Gmail 500/日,QQ 无明确上限)
API 简洁度 ⭐⭐⭐⭐⭐ 极简 · 3 行 axios POST ⭐⭐⭐ SDK 调用 · 需签名鉴权 ⭐⭐⭐⭐ nodemailer 5 行代码 N/A
凭据要求 1 个 API Key AccessKey + AccessSecret + 域名备案 + 发件人授权 SMTP host + port + user + password
域名备案 无需(可先用 onboarding@resend.dev 测试) 必须(国内 ICP 备案 + 阿里云邮件推送域名验证) 按 SMTP 服务商要求(Gmail/QQ/163 无需)
国内送达率 中等(依赖 DNS 路由) ⭐⭐⭐⭐⭐ 极高(阿里云基础设施) 不稳定(Gmail 可能被国内邮箱服务商标垃圾) N/A
国外送达率 ⭐⭐⭐⭐⭐ 极高 中等偏低(国际路由) 中等(看 SMTP 服务商) N/A
被标垃圾风险 低 · Resend 自动处理 SPF/DKIM/DMARC 低 · 阿里云自动配置 (Gmail/QQ 发来的邮件更易被拦) N/A
运维成本 极低 · 纯 SaaS 低 · 阿里云控制台 中(自管 SMTP 账号)
单价(超免费) $20 / 月 / 50K 封 ¥10 / 万封 免费(自建)/ 按 SMTP 服务商
合规性 海外服务 · 数据出境 国内服务 · 数据境内 看 SMTP 服务商所在地
M6.1 推荐度 ⭐⭐⭐⭐ 最快上手 · 无需备案 · 测试域名即用 ⭐⭐⭐ 若域名已备案则最佳 · 否则等待备案耗时 ⭐⭐ 最通用但易被拦 · 不推荐生产 ⭐⭐ 最简 · 但丢失 "管理层可见" 能力

3.3 典型代码示例对比

M1 Resend(最简):

await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.RESEND_API_KEY}` },
  body: JSON.stringify({ from: 'onboarding@resend.dev', to: OPS_EMAIL, subject, html })
});

M2 阿里云 DirectMail(中等复杂):需 @alicloud/dm20151123 SDK + AccessKey 签名,约 30 行。

M3 SMTP nodemailer(最经典):

const transport = nodemailer.createTransport({ host: 'smtp.qq.com', port: 465, secure: true, auth: { user, pass } });
await transport.sendMail({ from, to: OPS_EMAIL, subject, html });

M4 无邮件:只有 console.log('[M6.1-email-skipped]', leadData)

3.4 决策建议矩阵

您的情况 建议
官网未备案 · 想快速跑通 M1 Resend(测试域名即用)
域名已备案 · 国内客户为主 M2 阿里云 DirectMail
已有公司 Gmail/QQ 企业邮箱 M3 SMTP(但注意被标垃圾)
只想看到企微群推送即可 M4 暂不发邮件

4. 【B6 · 轻量 CRM 同步】作用说明 + 3 选项优劣对比

4.1 作用说明

CRM 同步的作用:除了 SQLite 存一份,是否同时写到一个"运营同事能直接看、能筛选、能导出"的外部表格?SQLite 文件对非技术同事不友好(需 DB Browser)。

在 M6.1 里的位置:这是可选功能,不影响 SQLite 主链路。M6.1 最小闭环可以只存 SQLite,后续再考虑外部同步。

4.2 3 选项优劣对比

维度 C1 · 不同步(SQLite only) C2 · 飞书多维表格 C3 · 企微机器人推送即可
实现复杂度 0 · 无额外工作 高 · 需 OAuth / app_id / app_secret / table_id 低 · 一个 Webhook URL 即可
运营查看方式 写个 /admin/leads 只读页(需做) 或 用 DB Browser 飞书 APP 内查表 · 可排序筛选导出 企微群看消息(滚动刷过即消失)
多人协作 ❌ 只能本地 ⭐⭐⭐⭐⭐ 飞书原生协作 ⭐⭐ 群消息会被刷走
筛选/排序/导出 ❌ 需自建 UI ⭐⭐⭐⭐⭐ 飞书原生 ❌ 无
历史追溯 ⭐⭐⭐⭐⭐ SQLite 永久保留 ⭐⭐⭐⭐⭐ 飞书永久保留 ⭐⭐ 企微仅保留 7 天
凭据准备 需飞书开发者账号 + 自建应用 + 授权 + 表格预建 1 个 webhook URL
国内可用性 N/A ⭐⭐⭐⭐ 飞书国内可用 ⭐⭐⭐⭐⭐ 企微国内强
成本 免费 免费(飞书免费版) 免费
数据主权 ⭐⭐⭐⭐⭐ 本地文件 字节跳动服务器 腾讯服务器
M6.1 推荐度 ⭐⭐⭐⭐ 最简 · 无负担 ⭐⭐⭐ 若已用飞书则最佳 ⭐⭐⭐⭐ 运营通知 + 群协作

4.3 三选项并非互斥

组合 效果
C1 only 只存 SQLite · 最简
C1 + C3 SQLite + 企微推送(推荐 minimum)
C1 + C2 + C3 全覆盖(SQLite 主存 + 飞书看板 + 企微群通知)

4.4 决策建议矩阵

您的情况 建议
早期 · 留资量小 · 只想跑通 C1(SQLite only)
公司用飞书 · 运营习惯用多维表格 C2(配置一次,长期受益)
公司用企微 · 不想引入飞书 C3(企微推送 · 与 B7 同源)

耦合提示

如果 B7 已选 W1(有企微 webhook),B6 选 C3 几乎零额外工作 —— 就是同一个 webhook 复用:发一条消息即可。


5. 【B7 · 企微机器人 Webhook】作用说明 + 3 选项优劣对比

5.1 作用说明

企微机器人的作用:每收到一条留资,在企微群里推一条消息(Markdown 格式),让群里的运营/销售立即看到。

在 M6.1 里的位置:独立副作用。即使未准备 webhook URL,代码也应写好 sendWeworkBot() 函数(URL 留空则 no-op · 控制台打印),M6.4 部署时填入 URL 即激活。

为什么重要:响应速度 >> 邮件(销售在群里 30 秒看到 vs 邮件 10 分钟才查)。

5.2 3 选项优劣对比

维度 W1 · 已有 webhook URL W2 · 暂无 · 写代码留空 no-op W3 · 本轮完全跳过
当前准备工作 0 · 填到 .env.local 即用 0 · 不需凭据 0 · 不写代码
M6.1 立即可用 ✅ 可收到消息 ❌ 仅控制台打印 ❌ 完全无
M6.4 迁移成本 0 · 改 .env.production 0 · 改 .env 即激活 高 · 需重写 sendWeworkBot
代码完整性 ⭐⭐⭐⭐⭐ 最完整 ⭐⭐⭐⭐⭐ 最完整(只是 URL 空) ⭐⭐ 缺一个副作用
降级友好度 N/A ⭐⭐⭐⭐⭐ 完美 · 无侵入 N/A
M6.1 推荐度 ⭐⭐⭐⭐⭐ 最佳 ⭐⭐⭐⭐ 足够 ⭐⭐ 不推荐(代码不完整)

5.3 创建企微机器人的步骤(若选 W1)

  1. 企业微信群 → 右上角 ⋯ → 添加群机器人 → 新建
  2. 配置名称 / 头像
  3. 复制 Webhook URL(形如 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx-xxx-xxx
  4. 填入 .env.localWEWORK_WEBHOOK_URL

5.4 典型消息格式

{
  "msgtype": "markdown",
  "markdown": {
    "content": "## 🎯 新线索\n- 姓名:张三\n- 公司:XX 车企\n- 邮箱:<zhang@xx.com>\n- 电话:13800000000\n- 留言:想了解 XiDSP 方案\n- 来源:<https://joysnd.com/products/xidsp>"
  }
}

5.5 决策建议矩阵

您的情况 建议
公司用企微 · 知道 webhook 怎么建 W1(10 分钟搞定)
公司用企微 · 先不折腾 webhook W2(代码先写好,后补 URL)
公司只用飞书 / 钉钉 W3(本轮跳过;B6 选 C2 飞书补偿)

6. 【C 类凭据】P1 + P2 · 处理"暂无"的降级策略

6.1 P1 · 邮件服务凭据

需要的凭据(因 B3 选项不同而异):

B3 选项 需要的凭据 降级行为(留空时)
M1 Resend RESEND_API_KEY console.log('[email-skipped] no RESEND_API_KEY', leadData)
M2 阿里云 ALI_DM_ACCESS_KEY + ALI_DM_ACCESS_SECRET + ALI_DM_REGION + ALI_DM_FROM_ADDR 同上降级打印
M3 SMTP SMTP_HOST + SMTP_PORT + SMTP_USER + SMTP_PASS 同上降级打印
M4 无 N/A

代码策略

// src/lib/email.ts
export async function sendEmail(data: LeadData) {
  const apiKey = import.meta.env.RESEND_API_KEY;
  if (!apiKey) {
    console.log('[M6.1-email-skipped] no RESEND_API_KEY', data);
    return { skipped: true };
  }
  // 真实发送...
}

6.2 P2 · 运营邮箱地址

需要的凭据:1 个接收通知的邮箱地址(如 ops@joysnd.com / sales@joysnd.com / 您自己的邮箱)

处理方式: - 提供真实地址 → 写入 .env.localOPS_EMAIL - 暂无 → 使用占位 ops@joysnd.com(代码不会报错 · 但真实发送会 404 · 降级打印也无伤大雅)


7. 项目结构与代码布局(M6.1 完成后的目标)

07_web/xisound-website/
├── astro.config.mjs               ← 修改:output 'static' → 'hybrid' · 加 @astrojs/node adapter
├── package.json                   ← 新增依赖:@astrojs/node · better-sqlite3 · zod(可选)
├── .env.example                   ← 新建 · 所有可能的环境变量
├── .env.local                     ← 新建 · 本地真实凭据(.gitignore)
├── data/                          ← 新建 · .gitignore
│   └── leads.db                   ← 运行时自动创建
├── src/
│   ├── lib/                       ← 新建目录
│   │   ├── db.ts                  ← 新建 · SQLite 连接 + schema 初始化
│   │   ├── email.ts               ← 新建 · 按 B3 选项实现 · 未配置则降级打印
│   │   ├── wework.ts              ← 新建 · 企微 webhook · 未配置则降级打印
│   │   └── lead-pipeline.ts       ← 新建 · 编排器(save → email → wework)
│   ├── pages/
│   │   ├── api/
│   │   │   └── lead.ts            ← 修改 · 移除 prerender=true · 实现 POST handler
│   │   └── admin/
│   │       └── leads.astro        ← 可选新建(若 B6=C1)· 只读表查看
│   └── types/
│       └── lead.ts                ← 新建 · LeadData + zod schema(可选)
└── tests/
    └── lead-api.spec.ts           ← 可选新建 · curl 模拟测试

8. M6.1 实现步骤(8 步 · 按顺序执行)

# 步骤 产出 预计耗时
1 准备:用户拍板 B3 / B6 / B7 选项 · 提供 P1/P2 凭据(可留空降级) 决策清单 + .env.local 10 min
2 装依赖npm i @astrojs/node better-sqlite3 + 按 B3 选项装邮件库 package.json + package-lock.json 5 min
3 改配置astro.config.mjsadapter: node({ mode: 'standalone' }) + output: 'hybrid' astro.config.mjs 5 min
4 建目录 + env 模板data/ + .env.example(列所有变量) + .env.local(真实凭据)+ .gitignoredata/ .env.local 3 个新文件 10 min
5 写 libdb.ts(SQLite 连接 + leads 表 CREATE IF NOT EXISTS)+ email.ts(按 B3)+ wework.ts(按 B7 · W2 时 URL 为空则 no-op) + lead-pipeline.ts(串联三者) 4 个 lib 文件 60-90 min
6 改 APIsrc/pages/api/lead.ts 去掉 prerender = true · 实现 POST · 调用 lead-pipeline · 返回 JSON 1 个文件 30 min
7 可选 /admin:若 B6=C1,建 src/pages/admin/leads.astro 只读页 · 简单 GET 查询 + 表格渲染 1 个文件 30 min
8 验证npm run dev 启动 · curl -X POST http://localhost:4321/api/lead -d '...' 验证三条链路 · 检查 SQLite 文件 + 邮件收件箱 + 企微群消息 验证通过 30 min

总计:3-4 小时(取决于 B3/B6/B7 选的复杂度)


9. 验证脚本(Step 8 详解)

9.1 启动

cd AlgoDepartment/07_web/xisound-website
npm install
npm run dev   # 默认端口 4321

9.2 模拟表单提交

curl -X POST http://localhost:4321/api/lead \
  -H "Content-Type: application/json" \
  -d '{
    "name": "测试客户",
    "company": "测试车企",
    "email": "test@example.com",
    "phone": "13800000000",
    "message": "想了解 XiDSP 方案",
    "source": "http://localhost:4321/products/xidsp"
  }'

9.3 期望结果

副作用 验证方式 期望
SQLite sqlite3 data/leads.db "SELECT * FROM leads ORDER BY id DESC LIMIT 1" 1 行返回
邮件(B3=M1) 查看 OPS_EMAIL 收件箱 收到新邮件
邮件(B3=M4 或 P1 留空) 查看控制台日志 [email-skipped] 打印
企微(B7=W1) 查看企微群 收到机器人消息
企微(B7=W2 或 URL 空) 查看控制台日志 [wework-skipped] 打印
HTTP 响应 curl 返回值 {"ok":true,"id":<number>}

10. M6.2-M6.4 延后项展望

阶段 目标 预计耗时
M6.2 账号系统(登录 / 注册 / session · 基于 Lucia 或 Auth.js) 3-5 天
M6.3 下载签名 URL(白皮书 / SDK 受保护资源 · JWT + 时限) 2-3 天
M6.4 生产部署(阿里云 ECS + Nginx + ICP + Vercel 海外 · PostgreSQL 升级) 5-7 天

M6.1 代码的前瞻设计:所有 lib(db / email / wework)都用依赖注入 + 接口抽象,M6.4 升 PostgreSQL 只需改 db.ts,其他不动。


11. 决策矩阵(请您回复)

# 决策点 选项 您的选择
B3 邮件服务 M1 Resend / M2 阿里云 / M3 SMTP / M4 不发邮件 ?
B6 轻量 CRM C1 SQLite only / C2 飞书多维表 / C3 企微推送即可 ?
B7 企微 webhook W1 已有URL(请附上)/ W2 写代码留空 / W3 跳过 ?
P1 邮件凭据 有就给 / 无就写 mock ?
P2 运营邮箱 真实地址 / 占位 ops@joysnd.com ?

建议回复格式

B3=M1, B6=C3, B7=W2, P1=无, P2=ops@joysnd.com

推荐组合示例

场景 B3 B6 B7 P1 P2
🚀 最小版 · 快速跑通 M4 C1 W2 - -
🎯 海外快手 · 无备案 M1 Resend C3 W2 Resend KEY ops@joysnd.com
🏠 国内生产 · 已备案 M2 阿里云 C3 W1 阿里云 AK/SK ops@joysnd.com
🧪 学习实验 · 全链路 M3 SMTP (QQ) C2 飞书 W1 SMTP 密码 + 飞书 token 个人邮箱

12. 风险与缓解

风险 缓解
better-sqlite3 是 native 模块 · Node 版本变化需重 build npm rebuild better-sqlite3 · 文档提示
M6.1 的 SQLite 文件在部署时可能被覆盖 .gitignore 排除 data/ · M6.4 部署策略独立处理
Astro 6 vs 4 的 API 差异 使用 Astro 6 标准 endpoint API · 不用已废弃的 get/post 导出方式
邮件被标垃圾(M3 SMTP) 建议用带 SPF/DKIM 的 M1/M2
企微消息频次超限 企微机器人限频 20 次/分钟 · M6.1 留资量小远低于此
.env.local 泄漏 .gitignore 必须包含 · 首次 commit 前检查

13. 相关文档


版本历史

版本 日期 变更
v1.0 2026-05-06 首次发布 · 列出 B3/B6/B7 三项决策的作用说明与选项优劣对比 · 不替用户拍板 · 等待决策矩阵回复

07_web · M6.1 最小可发布部署方案 · v1.0 · 2026-05-06 · © Xisound Inc.