BNEF活动管理平台
基于 React 19 + Supabase + 微信生态的活动管理 SaaS 平台,含 C 端(微信内浏览器)和 Admin 后台,支持报名、签到、电子票、通知等全流程。
概述
BNEF 活动管理平台是一个功能完整的活动管理 SaaS 系统,技术栈为 React 19 + TypeScript + Vite + Tailwind v4 + Supabase + 微信生态。项目仓库位于 ~/projects/BNEF(GitHub: Restry/BNEF.git),线上地址为 https://demo.dtask.net(原 bnef.restry.cn 已迁移)。
系统包含 20 个用户端页面和 13 个管理端页面,覆盖活动浏览、报名、签到、电子票、收藏、PC 端适配等功能。数据库使用 Supabase,已完成 11 次迁移。
技术栈
| 层面 | 技术 |
|---|---|
| 前端框架 | React 19 + TypeScript + Vite |
| UI 样式 | Tailwind CSS v4 |
| 后端/数据库 | Supabase(PostgreSQL + Auth + Edge Functions) |
| 微信集成 | 微信公众号 AppID wx225bf76b06064faa |
| 测试框架 | Playwright (E2E) + Vitest (单测) |
环境清单
| 环境 | 配置文件 | Supabase URL | 前端地址 |
|---|---|---|---|
| Dev(开发) | .env | https://db.dora.restry.cn | https://demo.dtask.net |
| Test(测试) | .env.test | https://restry-dev.chinanorth3.cloudapp.chinacloudapi.cn | — |
| SIT(集成测试) | .env.sit | https://tiger-host.chinanorth3.cloudapp.chinacloudapi.cn | — |
功能模块
用户端(20 页面)
- 首页(活动列表、分类筛选、已结束筛选)
- 活动详情(浏览量统计、倒计时、回放入口)
- 报名流程(动态表单、“其它”选项、隐私协议)
- 签到/电子票(二维码、搜索模式核销)
- 个人中心(报名记录、收藏、个人信息)
- PC 端适配(PCHome、PCEventDetail)
管理端(13 页面)
- 数据看板(活动数/报名数/会员数/浏览量)
- 活动管理(创建/编辑/发布/停止报名/隐藏/回收站)
- 报名管理(筛选/搜索/导出/拼音列)
- 通知系统(微信/短信模板选择、发送记录)
- 权限管理(角色、管理员创建/Ban/Unban)
- Banner 管理
测试体系
测试规模
- 单元测试: 228 个用例,7 个文件,全部通过
- E2E 测试: 179 个用例,17 个 spec 文件
- Smoke 测试: 14 个用例(本地)+ 线上 smoke
测试迭代过程(10 轮进化)
BNEF Bot 执行了 10 轮测试迭代,从”写死脚本”进化到”像真人一样测试”:
- 第 1-2 轮: 修复 13 个失败单测,跑通 Smoke
- 第 3-4 轮: 修复 E2E 选择器过时问题(24 个),通过核心链路
- 第 5-6 轮: 补充签到/电子票/PC 端/个人信息 26 个新用例
- 第 7-8 轮: 清理 37 个 skip(部分是旧 spec 指向不存在的路由)
- 第 9-10 轮: 探索性测试——像真人 QA 浏览页面截图发现问题
探索性测试发现的真实问题
| 严重度 | 问题 | 状态 |
|---|---|---|
| P1 | PC 端活动封面图 broken(img src="" 空值未处理) | ✅ 已修复 |
| P1 | placeholder 占位图还在用 via.placeholder.com | ✅ 已修复 |
| P2 | 报名页时间格式不一致(ISO vs 中文格式) | ✅ 已修复 |
| P3 | 活动详情页无浏览量显示 | ✅ 已修复 |
| P3 | Console 报 406 错误(PGRST116 未静默处理) | ✅ 已修复 |
| P3 | 首页无”已结束”状态筛选 | ✅ 已修复 |
测试覆盖缺口
| 模块 | 单测 | E2E | 风险 |
|---|---|---|---|
| auth.ts (529行) | ❌ 0 | 16 | 🔴 认证核心无单测 |
| notifications.ts (504行) | ❌ 0 | 1 | 🔴 通知基本裸奔 |
| Edge Functions (5个) | ❌ 0 | 0 | 🔴 零覆盖 |
| banners.ts / subscriptions.ts | ❌ 0 | 0 | 🟡 零覆盖 |
| components (14个) | ❌ 0 | — | 🟡 无组件单测 |
架构审查发现
P0 安全漏洞:service_role key 暴露到前端
VITE_SUPABASE_SERVICE_ROLE_KEY 通过 import.meta.env 被打包进客户端 JS。虽然生产环境 .env 中变量名无 VITE_ 前缀(实际未暴露),但测试环境 .env.test 有此前缀。
修复方案:新建 admin-user Edge Function 处理 createAdmin/ban/unban 三个操作,前端改为调 Edge Function,删除 supabaseAdmin 导出。已实施。
架构问题
| 问题 | 影响 |
|---|---|
| API 层单体聚合对象无法 tree-shake | Bundle 膨胀 |
| 45 个页面无代码分割 | 首屏加载慢(微信内浏览器) |
| CheckIn 路由权限不一致 | 未授权用户可访问签到页 |
| EventDetail.tsx 976 行 God Component | 26 个 useState,不可维护 |
| 错误处理三种风格混用 | 静默返回 [] 最危险 |
| 66 个 console.error 散落在 API 层 | 生产环境暴露内部信息 |
项目整理
已清理
- 17 个一次性探索脚本 + 72MB 临时文件
- 过时文档:rollback-plan.md、changelog.md、Bug-list.md
- 合并重复测试文档(test-case.md → 删)
- 删除过时 POM 页面和 spec 文件
- 删除无引用的 src/api/core.ts
docs/ 目录现状
| 文件 | 内容 |
|---|---|
| prd.md | 完整 PRD(1707 行) |
| e2e-test-cases.md | E2E 自动化测试案例清单 |
| test-plan.md | 测试计划(已更新) |
| test-report-20260321.md | 测试报告 |
时间线
- 2026-03-21: 项目整理,确认技术栈和环境清单
- 2026-03-21~22: 10 轮 E2E 测试迭代,从 13 个失败到 0 failure
- 2026-03-22: 探索性测试发现并修复 6 个真实问题
- 2026-03-22: 项目文件清理(72MB 垃圾 + 过时文档)
- 2026-03-23: 架构代码审查,发现并修复 P0 安全漏洞
- 2026-03-24: 代码推送,PackHorizon-AI 部署
2026-04-20 更新:Supabase 已下线 + 后端栈迁 FastAPI + 部署改 Caddy
架构迁移确认(Daddy 04-20 钉死):项目早已不再使用 Supabase,后端切到自建 FastAPI + PostgreSQL,反代用 Caddy(Caddyfile.app) 而非 nginx。所有早期”Supabase Edge Functions / Auth / DB”的描述均已废弃。
| 现役 | 历史 |
|---|---|
| FastAPI + PostgreSQL(自建) | |
Caddyfile.app(全局 6 条安全头) | nginx.conf |
scripts/deploy.sh --skip-seed 一键部署 | scripts/deploy.dev.sh git push origin HEAD:main + scp Supabase functions |
主分支 refactor/fastapi-backend → 已合 main(96 commit fast-forward) | main |
公网 https://demo.dtask.net | 部署 host:restry@restry-dev.chinanorth3.cloudapp.chinacloudapi.cn:18822,路径 ~/projects/BNEF/ |
Caddy + 双容器(前端 + bnef-backend)+ uploads volume | — |
测试账号:admin admin@bnef.test / admin123;前台 user2@bnef.test / user123、user4@bnef.test / user123(实测 200)。
2026-04-20 UI/UX 审计 + 19 项第三方测试修复
Claude Code 全栈 UI/UX 审计(docs/audit/ui-ux-audit-2026-04-20.md)
5 Critical / 10 High / 9 Medium / 6 Low。反 AI slop 通过(无紫粉渐变、无渐变文字、无 hero 大数字),但有 3 个本地反模式:① 玻璃拟态过密(32 处 backdrop-blur)② 圆角卡片一刀切缺信息层级 ③ 主色三套并存(#00D2B3 / blue-600 / teal-500)。Top 5 Critical:C1 隐私默认勾(个保法 14 条)、C2 AdminLogin 配色脱钩 + 对比度 2.6:1、C3-C5 略。
修复轮(commit 6d33d78):19 个文件,覆盖 C2/C3/C4/C5 + H1-H10。约束严格 — 跳过 C1(业务)、window.confirm → Modal 时回调一字不变、不动 API/状态机/路由/权限/表单字段、不新增依赖、build 通过。踩坑:C5 PersonalInfo 邮箱改动取消分支被写成 throw / reject Promise → 被外层 catch 当成”保存失败”弹错误 Modal、email 字段未回滚。E2E 端到端补测才暴露(pytest 测不出)。
第三方测试 22 项 → 19 项修复(4 批次)
docs/site-test-fixes-2026-04-20.md 落清单。Daddy 排除 8/10/20(设计如此),剩 19 条按主题分批:
| 批次 | 内容 | Commit |
|---|---|---|
| A · 后台权限与鉴权安全 | 5 条(审核员越权、自定义角色权限不生效、/roles 未登录可访问、Mixed Content、ProtectedRoute 多挡一层) | 9f4d19d + 8840c3c |
| B · 通知链路 | 5 条(/notifications?userId= 跨用户、SMS template_code、微信 47003、审核重复生成、1h 提醒未生成) | 6d8416e |
| C · 报名与前台展示 | 3 条 | 449a9bf |
| D · 前端告警与体验 | 6 条(D5 安全头按 Daddy 纠正改 Caddyfile.app,nginx.conf git rm) | 4d6d22d + cca7dca |
E2E 补测 15/15 PASS(Browser Agent 技能 4821/4822 端口;禁用 playwright/puppeteer 自起浏览器)。新增 2 个 alembic migration + 13 个 pytest 全过。
工作流教训
- Hermes 不写代码 — 所有代码改动派 Claude Code(
zsh -ic 'claude -c --dangerously-skip-permissions --print "$(cat /tmp/...)" < /dev/null'),Hermes 负责 prompt + 验证 + 对话 - Browser Agent ≠ playwright — Claude Code 端到端验证必须用 Hermes 的 browser-agent 技能(4821 默认实例 / 4822 第二实例),它一度自起 playwright 跑测试被 Daddy 纠正
- 服务器端有 121 个未 commit 改动 的灾难现场 — Claude Code 正确停下没盲目 rsync,先
tar备份(BNEF-backup-20260420-135943.tar.gz285KB)再git clean -fd+ checkout 切到refactor/fastapi-backend重建 - 部署文档与脚本补齐:
docs/deploy/README.md+scripts/deploy.sh(环境变量可覆盖、set -euo pipefail、部署后 curl 验证)+ 根 README 入口
2026-04-21 第二轮回归 + 性能扩容 + 通知中心恢复
源:fries-mac (lewaymacmini-3-local) 全天派 Claude Code 推进。
P0 回归 — C5 PersonalInfo 邮箱取消分支
04-20 修复后 13 项测试通过、安全头全过,但 C5 邮箱改动「取消」分支 throw / Promise.reject 被外层 catch 捕成”保存失败”弹错误 Modal、email 字段未回滚。当天派 Claude Code 修代码 + Browser Agent 端到端验证。Daddy 当场强化规则:所有涉及浏览器访问功能的代码改动必须用 Claude Code 的 browser-agent 技能跑端到端集成测试,不能让用户自己跑验证。
性能优化 + PostgreSQL 入 Docker Compose
第二轮把 PostgreSQL 也搬进同一份 docker-compose(与 backend / 前端 / Caddy 同栈),demo 数据不迁移直接以新库重建。本地无 Docker → 改完直接派 Claude Code 上测试环境跑。单 Worker 限制确认是定时任务的约束(非性能上限)。
第二轮 site-test 修复(22 → 19 → P2 批次)
site-test-report-2026-04-20-real-browser-structured.md 上来后分 2 批改:
| 批次 | 处理 |
|---|---|
| 1 | Daddy 直接 1A / 2A / 3 批准;改前要求 Claude Code 先验证问题确实存在再动 |
| 2 | P2 列表逐项点选 — P2-2 报名状态 不改、P2-4 表单保存后跳列表 改、P2-4 图表告警 改;其余 Claude Code 自验是否真问题 |
Redis 不动。Round 2 收完 push。
恢复 Notification 管理页(仅管理员)
晚上回头补:通知管理页(待发送 / 发送记录)以前删过,改回管理员专属页面 + 功能审计补 5 项缺口(Daddy “5 个都做”)。
工作流硬规则(2026-04-21 锁死)
- Hermes ≠ 写代码:所有代码改动派 Claude Code,命令模板
source ~/.zshrc && cd ~/projects/BNEF && claude -c --dangerously-skip-permissions --print "$(cat /tmp/xxx.txt)"。 - Claude Code ≠ 用官方账号:必须先
source ~/.zshrc加载 Copilot→Anthropic API URL;当天踩坑后 Daddy 命令把官方登录 token 退出。 - 改 → E2E:每次代码修改强制 Claude Code 接 browser-agent 跑端到端,禁用 playwright/puppeteer 自起浏览器。
- 改前先验证:Claude Code 不能盲改,先确认问题确实存在、值得修,再动手。
- 批量改前:先检查本地代码全部 commit + 远程 demo 部署是最新。
2026-04-21 补量(fries Session 1/3)
双网段 502 修复 45e19dd
bnef-app(前端 Caddy 容器)只挂在 bnef_default,但 backend 容器挂 supabase_default(外部网络),导致前端反代 backend 502。修复:把 bnef-app 也接入 supabase_default 双网段,Caddy 直接按容器名解析 backend。
真实通知链路验证(无 mock)
WeChat 模板消息 + 阿里云 SMS 端到端打通:
| 通道 | 凭证/实测 |
|---|---|
| errcode=0;曾遇 47003(模板字段不匹配)/ 41033(无 openid 绑定)/ user_no_openid | |
| SMS | 实测 BizId 3218063767 / 3229152767 / 4096168767 全部回执成功 |
APScheduler 5 分钟轮询 + scheduled_at=NULL 立即派发;APScheduler 跑 in-process,所以 backend 必须 single uvicorn worker(同时也是 in-memory rate limit 的约束)。
24h / 1h 双通道提醒 8c0df89
原设计仅 WeChat 推送(产品决策),04-21 改为 24h 与 1h 双时点 + WeChat / SMS 双通道并行下发。system_config 13 条配置从 supabase-db.bnef_test 一次性迁到自建 PG,断 Supabase 最后一根线。
Round 3 FIX-1..5
| FIX | Commit | 内容 |
|---|---|---|
| 1 | e2e38b9 | — |
| 2 | 04f16f9 | — |
| 3 | 1f42663 | — |
| 4 | bfb682d | — |
| 5 | 1fa138f | 收尾,HEAD 推到 demo |
HTML 坑:
<input type=number max={N}>触发浏览器 constraint validation,会静默拦住 form submit。Hermes 起 skillhtml-constraint-validation-gotcha记下,配套bnef-qa-fix-workflow把”验证 → 改 → E2E”流程模板化。
Notification 管理页恢复 + 5 项增强(7320a81 → eebb4c5)
通知管理页(管理员专属,删过一次现回灌)补 5 项功能:
- SSE 实时流:用
fetch + ReadableStream而非EventSource(后者无法带 Bearer),Caddy 侧加flush_interval -1才能把 SSE 实时推到浏览器,否则被反代缓冲堆住 - 24h AreaChart 趋势
- 7d Top3 PieChart
- 失败重试:响应里带
result.retry_of关联原始记录 - 服务端分页
Token key 隔离:bnef_admin_access_token(admin)vs bnef_access_token(client),避免管理后台与 C 端 SWR 缓存互相污染。
Claude Code 凭证踩坑(fries Session 3)
macOS Keychain 里的 Claude Code OAuth token 优先级高于 ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY 环境变量——Claude Code 一直走官方账号扣费、不走 Copilot 代理。Daddy 命令清掉:
security delete-generic-password -s "Claude Code" -a "leway"
清完才回到 zshrc 里的代理 base url / key。详见 copilot-anthropic-proxy。
相关页面
- bnef — BNEF Bot 实体
- supabase-platform — Supabase 平台
- caddy-reverse-proxy — Caddy 反向代理
- azure-vm-management — Azure VM 管理
- copilot-anthropic-proxy — Copilot→Anthropic 代理