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(开发).envhttps://db.dora.restry.cnhttps://demo.dtask.net
Test(测试).env.testhttps://restry-dev.chinanorth3.cloudapp.chinacloudapi.cn
SIT(集成测试).env.sithttps://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. 第 1-2 轮: 修复 13 个失败单测,跑通 Smoke
  2. 第 3-4 轮: 修复 E2E 选择器过时问题(24 个),通过核心链路
  3. 第 5-6 轮: 补充签到/电子票/PC 端/个人信息 26 个新用例
  4. 第 7-8 轮: 清理 37 个 skip(部分是旧 spec 指向不存在的路由)
  5. 第 9-10 轮: 探索性测试——像真人 QA 浏览页面截图发现问题

探索性测试发现的真实问题

严重度问题状态
P1PC 端活动封面图 broken(img src="" 空值未处理)✅ 已修复
P1placeholder 占位图还在用 via.placeholder.com✅ 已修复
P2报名页时间格式不一致(ISO vs 中文格式)✅ 已修复
P3活动详情页无浏览量显示✅ 已修复
P3Console 报 406 错误(PGRST116 未静默处理)✅ 已修复
P3首页无”已结束”状态筛选✅ 已修复

测试覆盖缺口

模块单测E2E风险
auth.ts (529行)❌ 016🔴 认证核心无单测
notifications.ts (504行)❌ 01🔴 通知基本裸奔
Edge Functions (5个)❌ 00🔴 零覆盖
banners.ts / subscriptions.ts❌ 00🟡 零覆盖
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-shakeBundle 膨胀
45 个页面无代码分割首屏加载慢(微信内浏览器)
CheckIn 路由权限不一致未授权用户可访问签到页
EventDetail.tsx 976 行 God Component26 个 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.mdE2E 自动化测试案例清单
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(自建)Supabase
Caddyfile.app(全局 6 条安全头)nginx.conf(整文件 04-20 git rm,无人引用)
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 / user123user4@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.appnginx.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.gz 285KB)再 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 批改:

批次处理
1Daddy 直接 1A / 2A / 3 批准;改前要求 Claude Code 先验证问题确实存在再动
2P2 列表逐项点选 — 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 端到端打通:

通道凭证/实测
WeChaterrcode=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

FIXCommit内容
1e2e38b9
204f16f9
31f42663
4bfb682d
51fa138f收尾,HEAD 推到 demo

HTML 坑:<input type=number max={N}> 触发浏览器 constraint validation,会静默拦住 form submit。Hermes 起 skill html-constraint-validation-gotcha 记下,配套 bnef-qa-fix-workflow 把”验证 → 改 → E2E”流程模板化。

Notification 管理页恢复 + 5 项增强(7320a81eebb4c5

通知管理页(管理员专属,删过一次现回灌)补 5 项功能:

  1. SSE 实时流:用 fetch + ReadableStream 而非 EventSource(后者无法带 Bearer),Caddy 侧加 flush_interval -1 才能把 SSE 实时推到浏览器,否则被反代缓冲堆住
  2. 24h AreaChart 趋势
  3. 7d Top3 PieChart
  4. 失败重试:响应里带 result.retry_of 关联原始记录
  5. 服务端分页

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

相关页面