透明、卫生、无摩擦的寝具循环体验。发现经过深度紫外真空净化的优质二手大牌床垫,支持 15 天卫生安心审核,楼宇电梯托运直配。
技术栈:Next.js 14 (App Router) · TypeScript · Supabase (PostgreSQL + Storage) · Tailwind CSS 当前阶段:MVP 已完成,核心功能闭环,生产构建通过(18 路由)
- 落地首页 — Hero / 信任数据 / 流程说明 / 材质网格 / 用户评价 / CTA
- 城市浏览 — 50+ 城市、IP 自动定位(H5 优先 + 高德兜底)、动态路由、SEO
- 搜索与筛选 — 关键词、价格区间滑块、尺寸 / 材质 / 成色多维筛选、排序、骨架屏、空态
- 商品发布 — 完整表单、图片上传(Supabase Storage,兜底 base64)、Server Action 写入
- 商品详情 — 图片画廊、规格参数、联系方式按需获取(防隐私泄露)
- 卖家管理 — 编辑 / 删除 / 上下架 / 一键降价(触发收藏用户通知)
- 收藏管理 — localStorage + Supabase 双端同步
- 消息通知 — 联络申请、降价通知、全部标已读
- 鉴权 — HttpOnly Cookie (device_id) + Supabase RLS 行级安全
- 暗色模式 — class 策略、Header 主题切换、全组件
dark:适配 - 图片优化 — 全量
next/image迁移 - 降级策略 — Supabase ↔ localStorage 三层降级,断网/超时仍可用
- Node.js ≥ 18.17
- npm
- 一个 Supabase 项目(提供数据库与对象存储)
- 高德开放平台 Web 服务 Key(用于 IP 定位与逆地理编码)
npm install复制示例文件并填入真实凭据:
cp .env.example .env.local然后在 .env.local 中填入以下配置(各项说明见 .env.example):
| 变量 | 必填 | 用途 |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
✅ | Supabase 项目 URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
✅ | Supabase 匿名密钥(前端可见,配合 RLS) |
SUPABASE_SERVICE_ROLE_KEY |
✅ | Supabase 服务端密钥(绕过 RLS,仅服务端;城市同步/seed 用) |
GAODE_IP_KEY |
✅ | 高德 Web 服务 Key,IP 定位 + 逆地理编码 |
APP_URL |
✅ | 应用主域名,用于回调与自引用链接 |
ADMIN_SYNC_KEY |
✅ | 保护 /api/cities/sync 的管理员密钥 |
GEMINI_API_KEY |
○ | Gemini AI(按需,未启用可留空) |
- 打开 Supabase Dashboard → SQL Editor
- 执行建表脚本:
src/utils/supabase/schema.sql(首次部署) - 执行增量迁移:
supabase/migrations/20260617000001_add_listings_missing_columns.sql(补齐listings表的isActive/updatedAt/sellerDeviceId列)
npm run seed:cities该脚本将全国 358 个城市写入 cities 表。也可改用管理后台 /admin/cities 在线维护。
npm run dev| 命令 | 说明 |
|---|---|
npm run dev |
启动开发服务器 |
npm run build |
生产构建 |
npm run start |
启动生产服务器(需先 build) |
npm run lint |
ESLint 检查 |
npm run seed:cities |
城市数据种子脚本(写入 Supabase) |
src/
├── app/ # Next.js App Router
│ ├── page.tsx # 落地首页
│ ├── [city]/ # 城市浏览页(动态路由 + generateMetadata)
│ ├── listing/[id]/ # 商品详情页
│ ├── post/ # 商品发布页
│ ├── me/ # 个人中心(收藏/我的发布/消息)
│ ├── admin/cities/ # 城市管理后台
│ ├── about/ safety/ terms/ # 静态页
│ ├── api/ # API 路由
│ └── actions.ts # Server Actions(发布/更新/删除商品)
├── components/ # React 组件
│ └── home/ # 首页区块组件
├── config/ # 配置(城市映射、默认数据、床垫规格)
├── context/ # 全局 Context(城市/收藏/通知)
├── hooks/ # 自定义 Hooks
├── lib/ # 城市数据访问层
├── utils/
│ ├── supabase/ # Supabase 客户端(server/client/public/service-role)
│ ├── db.ts # 数据访问(含降级策略)
│ ├── auth.ts geo.ts upload.ts
│ └── geo/ # 地理位置工具
├── middleware.ts # Supabase session 刷新 + device_id cookie
└── types.ts # TypeScript 类型定义
supabase/
└── migrations/ # 增量迁移 SQL(幂等)
scripts/
└── seed-cities.ts # 城市种子脚本
docs/ # 设计文档与需求分析
完整设计文档位于 docs/:
- 项目主页 — 功能完成状态与路线图
- 需求分析 — 用户故事 · 功能需求规格说明书
- 系统设计 — 架构 · 数据库 · API · UI/UX · 鉴权 · 数据流与降级 · 城市定位改进
- 技术选型 — 开发环境搭建 · 编码规范
所有数据访问走 src/utils/db.ts,三层降级保证可用性:
Supabase(主)─超时/报错─→ 默认 mock 数据(DEFAULT_LISTINGS)
- 服务端查询带 5s 超时(
withTimeout),失败自动降级 - 图片上传优先 Supabase Storage,失败降级 base64 + localStorage(含配额保护)
- 收藏/通知 localStorage 与 Supabase 双端同步
- 设备标识
device_id存于 HttpOnly Cookie,中间件统一注入 - Supabase RLS 通过
get_device_id()读取请求头x-device-id做行级权限校验 - 卖家仅能更新/删除自己的商品(
sellerDeviceId比对) - 联系方式(微信/手机)按需获取,不在列表/详情直接暴露
推荐部署到 Vercel:
- 导入 GitHub 仓库
- 在项目设置中配置全部环境变量(同
.env.example) - 部署前确保已执行数据库建表与迁移脚本
- 将
APP_URL改为生产域名
数据库初始化脚本(schema.sql / migrations)需在 Supabase Dashboard 手动执行;Vercel 不会自动运行 SQL。
| 优先级 | 方向 |
|---|---|
| P1 | 真实用户认证(微信 / 手机号登录替代 device_id 匿名追踪) |
| P1 | 图片存储全面迁移至 Supabase Storage(淘汰 base64 兜底) |
| P2 | 测试覆盖(单元测试 + E2E) |
| P2 | CI/CD 流程 |
详见 docs/项目主页.md。