Commit Graph

126 Commits

Author SHA1 Message Date
kurihada bf806f0891 fix: 改用 sqlite3 原生工具做 schema 同步,替代不可靠的 prisma CLI 方案 2026-02-28 17:33:49 +08:00
kurihada 3e7bb51618 fix: 房间加载失败时显示具体错误原因,便于线上排查 2026-02-28 17:30:09 +08:00
kurihada 039887547d fix: 容器启动时自动同步数据库 schema,解决线上部署后表结构不同步导致的加载失败 2026-02-28 17:24:03 +08:00
kurihada 1e39c72a63 fix(blindbox): 修复房间创建后返回大厅不可见 + 大厅房间列表管理
- 修复房主退出房间时误删整间房的问题,改为返回大厅(房间保留)
- 修复大厅页 fetchRooms 时序依赖导致导航回来不刷新的问题
- fetch 添加 cache:no-store + router.refresh() 确保数据始终最新
- 房间列表增加 max-h 滚动 + 底部渐变遮罩防溢出
- 大厅房间卡片支持内联删除/退出(··· 按钮 → 确认栏)
- rooms API 返回 creatorId 以区分房主/成员操作
2026-02-27 18:38:05 +08:00
kurihada 2d49744dd0 feat(blindbox): AI 计划生成流式进度与渐进式文案
- 新增 runPlanGeneration 与 onProgress 回调 (blindboxPlanGen.ts)
- 新增 POST /api/blindbox/plan/stream 推送 SSE 进度事件
- 前端优先走流式接口,实时展示「分析想法→搜索地点→规划路线→快好了」
- 流式失败时回退普通 POST,客户端轮播进度文案作为后备
- 规划阶段 UI 显示 planStatusMessage 替代静态文案
2026-02-27 17:37:40 +08:00
kurihada 9aee4f0e9b fix(blindbox): 周末契约页输入框无法输入 — 非受控输入 + 全局 input 样式
- 创建/加入房间输入改为 ref + defaultValue,提交时从 DOM 取值,避免受控 state 导致无法打字
- globals: input/textarea 增加 user-select: text !important、touch-action: manipulation
- Input 组件: cursor-text、touch-manipulation、min-w-0、autoComplete=off
2026-02-27 17:27:51 +08:00
kurihada 61ef54b2bd feat: 盲盒想法输入增加 AI 灵感推荐
输入框下方展示灵感建议标签,点击即填入,降低创作门槛:
- 房间有 ≥2 条想法时调用 DeepSeek 生成贴合调性的推荐
- 想法不足或 AI 失败时 fallback 到 18 条静态灵感随机选 4 条
- 提交新想法后自动刷新推荐,支持手动换一批
2026-02-27 16:52:59 +08:00
kurihada 76349f0dcf feat: 接入全站图片资源 + 修复卡片滑动与房间轮询问题
图片资源接入:
- OG/Twitter 社交分享元数据 (og-image.png)
- 错误页插画替换图标 (error-robot.png)
- EmptyState 组件支持 image prop,空状态页面接入插画
- 餐厅图片 fallback 改用 restaurant-fallback.png
- 极速救场/周末契约页面添加 hero 装饰图
- 分享卡片添加背景图层 (share-bg-*.png),通过 base64 预加载
- 更新 App 图标 (apple-touch-icon, icon-192/512)

Bug 修复:
- SwipeDeck: swipe action 从 "nope" 改为 "pass",匹配 API 预期
- SwipeDeck: 用 ref 读取 currentIndex 避免竞态重置(本地滑动后
  被服务端旧 swipeCounts 立即清零)
- SwipeDeck: 卡片 key 加入 isTop 标识,强制 remount 解决
  framer-motion drag 手势在 isTop 切换时不重新初始化的问题
- SwipeableCard: initial 统一为背景位置,确保晋升为顶部卡片时
  有一致的放大动画
- useRoomPolling: roomId 为空时跳过 SWR 和 EventSource
- room page: joinRoom 前 guard roomId,消除退房时 404
- layout: 添加 metadataBase 消除 Next.js OG 图片警告
2026-02-27 16:08:38 +08:00
kurihada 4073be9066 fix: invite 页面补充 Utensils 图标导入,修复编译失败 2026-02-27 11:43:40 +08:00
kurihada 3409456ca5 merge: dev → main (v2.0 周末契约 + 全站重构) 2026-02-27 10:48:13 +08:00
kurihada 3335f7f872 refactor: 移除 Service Worker 离线缓存 + 统一注册引导文案
- 删除 sw.js、ServiceWorkerRegistrar、offline 页面
- 保留 manifest 和 PWA 图标(添加到主屏幕仍可用)
- 注册引导文案统一为"10 秒注册,无需手机号"
2026-02-27 10:38:33 +08:00
kurihada 4cc9d66403 fix: SW 缓存导致页面不更新 — fetch 绕过 HTTP 缓存 + hydration 闪屏修复
- sw.js: HTML fetch 加 cache:"no-cache",避免从浏览器缓存拿旧页面
- ServiceWorkerRegistrar: 注册时 updateViaCache:"none",确保 SW 自身及时更新
- blindbox/page: 增加 hydrated 状态,防止 SSR/CSR 内容不一致导致闪屏
2026-02-27 10:28:23 +08:00
kurihada beb2c632bb refactor: 移除个人页面决策记录,统一由成就墙展示
决策记录已完整迁移至 /achievements 成就墙页面,
个人中心不再重复展示,减少冗余数据请求。
2026-02-27 10:05:22 +08:00
kurihada c17b13b6a8 feat: 契约生命周期 + 到期通知 + 成就墙
- 扩展 WeekendPlan schema: 新增 endTime 字段与 userId 索引
- PATCH /api/blindbox/plan 支持 accept/complete/expire 操作,
  接受时自动计算契约结束时间
- GET /api/blindbox/plan 支持 mode 参数 (latest/pending/history)
- 房间页接受契约后自动返回想法池,顶部显示"契约进行中"指示器
- 契约到期时触发浏览器通知 + 页面加载时弹出完成确认弹窗
- 新增 /achievements 成就墙页面:统计数据 + 决策记录/契约记录双标签
- 首页和个人中心新增成就墙入口
2026-02-27 02:12:18 +08:00
kurihada d8e42b860f feat: 已接受契约持久化 — 保存/加载/自动展示
- PATCH /api/blindbox/plan: 接受契约时更新状态为 accepted
- GET /api/blindbox/plan: 查询房间内最近一次已接受的计划
- 进入房间时自动加载已接受计划并展示行程视图
- 修复整个周末想法不足时重复活动的问题(不足则只生成一天)
2026-02-27 01:57:42 +08:00
kurihada 9c680ec11e feat: AI 周末行程规划 — DeepSeek 智能排期 + 高德 POI + 多日翻页
- 接入 DeepSeek API,提交想法时自动 AI 打标(品类/时段/时长/搜索策略)
- 新增行程规划 API:智能选取想法 → 高德 POI 搜索 → AI 生成最优行程
- 支持多日计划("整个周末"拆分周六/周日,并行 AI 调用)
- 行程展示逐日翻页,时间线可滚动,操作按钮固定底部
- 分享卡适配多日格式,支持图片保存与原生分享
- Prisma schema 新增 WeekendPlan 模型及 BlindBoxIdea AI 标签字段
- Jenkinsfile 集成 DEEPSEEK_API_KEY 环境变量
2026-02-27 01:51:47 +08:00
kurihada 8c6da410ca style: 周末契约顶部对齐极速救场 + 首页卡片样式统一
- blindbox/page: 加 justify-center 让标题垂直位置与 panic 页保持一致,移除 flex-1 子容器
- page: 周末契约卡片色值统一用 white/* 透明度,去掉紫色 drop-shadow,简化 hover 光晕
2026-02-27 00:12:58 +08:00
kurihada fbb2c22964 feat: 场景细分为餐厅/咖啡奶茶/甜品 + 附近搜索优先 + 美食多选标签 2026-02-26 23:04:25 +08:00
kurihada c0662de3dd feat: 美食多选 + 自定义输入标签 2026-02-26 21:40:14 +08:00
kurihada ab55f0981e feat: 地点搜索附近优先 — 透传用户坐标给高德 inputtips 2026-02-26 21:00:48 +08:00
kurihada 838f98b48b docs: 更新 BUGFIX.md 标记 Low 优先级 #27-#31 为已完成,BUGFIX.md 全部清零 2026-02-26 20:26:13 +08:00
kurihada deba7ab2bb fix: AudioContext 复用 + 盲盒加载错误提示 + icon 按钮 aria-label
- #27: panic/blindbox/ShareCardModal/AuthModal 中 icon-only 按钮补 aria-label
- #28: playChime 缓存复用单个 AudioContext,避免超出浏览器 ~6 个上限
- #29: 已完成(上一批次 ApiError.name = "ApiError")
- #30: blindbox lobby fetchRooms 失败时显示"加载失败/点击重试"
- #31: 已完成(theme.ts VALID_THEMES 校验)
2026-02-26 20:25:56 +08:00
kurihada f4a8fd7fee docs: 更新 BUGFIX.md 标记 Medium 优先级 #15-#26 为已完成 2026-02-26 20:22:30 +08:00
kurihada 5adfe8d3f1 fix: 客户端资源清理 — ShareCard 依赖 + 定时器 + 死代码
- #20: ShareCardModal useEffect 依赖改为 imageSrc 字符串,避免对象引用变化重复加载
- #21: BlindboxRoomPage 所有 setTimeout/rAF 统一收集,unmount 时清理
- #25: 删除未使用的 confettiCanvasRef 和 canvas 元素
2026-02-26 20:21:59 +08:00
kurihada cf88d3a1d2 fix: 外部 API 错误处理 + 导航 URL 校验 + reset/narrow 错误反馈
- #22: 高德 API fetch 加 try/catch,失败返回 503 而非泛化 500
- #23: buildNavUrl 校验 location.split(",") 结果长度和非空
- #24: handleReset/handleNarrow 检查 res.ok,失败时 toast 提示
2026-02-26 20:20:59 +08:00
kurihada dfb3cfa136 fix: 服务端验证强化 — 房间ID/坐标/swipe/盲盒竞态/空格
- #15: 房间 ID 扩展为 6 位字母数字,createRoom 用 P2002 重试替代 find-then-create
- #16: 盲盒编辑/删除改用 updateMany/deleteMany 原子操作,防止 TOCTOU
- #17: lat/lng 用 Number.isFinite + 范围校验 (-90~90, -180~180)
- #18: swipe action 必须为 'like' 或 'pass'
- #19: user PUT 的 JSON.parse(preferences) 加 try/catch
- #26: requireString 拒绝纯空格字符串
2026-02-26 20:19:56 +08:00
kurihada 93f20747e4 docs: 更新 BUGFIX.md 标记 High 优先级 #6-#14 为已完成 2026-02-26 20:16:54 +08:00
kurihada 50ae46fe15 fix: AuthModal 重开时重置表单 + 邀请页加入失败显示错误
- #13: useEffect 监听 open 变化时重置所有表单状态(用户名、密码、错误等)
- #14: handleJoin catch 中捕获错误消息并渲染到页面
2026-02-26 20:16:25 +08:00
kurihada 508903b67d fix: SSE 认证 + 收藏去重 + 数据库索引和级联删除
- #7: SSE events 接口校验 userId 房间成员身份,start() 加 try/catch
- #9: Favorite 新增 restaurantId 字段做精确去重,不再用 JSON contains
- #10: 补齐 Decision/Favorite/Room/BlindBoxIdea 缺失索引
- #11: Decision/Favorite/BlindBoxMember/BlindBoxIdea 加 onDelete Cascade
2026-02-26 20:15:45 +08:00
kurihada 9c7f18e0fa fix: 用户名唯一性竞态处理 + 密码长度上限 + JSON.parse 安全
- #6: register/user PUT 捕获 P2002 返回 409,apiHandler 全局兜底
- #8: GET /api/user 的 JSON.parse(preferences) 加 try/catch 防崩溃
- #12: 密码校验加 128 字符上限防 DoS
- #29: ApiError.name 设为 "ApiError" 便于调试
2026-02-26 20:14:02 +08:00
kurihada 6488c19172 fix: theme.ts 所有函数添加 SSR 守卫,校验 localStorage 合法值
- setStoredTheme/applyTheme/initTheme 添加 typeof window 检查
- getStoredTheme 校验存储值是否在白名单中,防止非法 theme 值
2026-02-26 20:11:21 +08:00
kurihada c4d1a122b2 fix: fetch 后检查 res.ok 并校验数组类型,防止错误响应导致崩溃
- panic/page.tsx: suggestions fetch 检查 res.ok + Array.isArray
- profile/page.tsx: history/favorites fetch 检查 res.ok + Array.isArray
2026-02-26 20:10:59 +08:00
kurihada 5783d522b7 fix: SwipeDeck 检测他人重置后自动清空本地状态
当 server 端 swipeCount 归零而本地 currentIndex > 0 时
说明其他用户执行了重置,自动调用 clearLocalState()
2026-02-26 20:10:29 +08:00
kurihada b9a07f84c0 fix: 盲盒抽取使用事务 + 乐观锁防止并发抽到同一想法
- 将 findMany + update 包裹在 prisma.$transaction 中
- 使用 updateMany({ where: { id, status: "in_pool" } }) 保证原子性
- count=0 时返回 409 提示用户重试
2026-02-26 20:10:04 +08:00
kurihada 90d3b35069 fix: 房间 swipe/undo/reset 接口添加成员身份校验
- swipe/undo:校验 userId 在 data.users 中,非成员返回 403
- reset:要求传 userId,校验为房间成员或创建者
- 客户端 handleReset/handleNarrow 传入 userId
2026-02-26 20:09:40 +08:00
kurihada 2a3cef890c refactor: 拆分 MatchResult、ProfilePage、BlindboxRoomPage 大组件
- MatchResult: 提取 NoMatchResult、RunnerUpCard(635 → 513 行)
- ProfilePage: 提取 ProfileHistoryCard、ProfileFavoritesCard(692 → 526 行)
- BlindboxRoomPage: 提取 BlindboxMyIdeas、BlindboxDrawnHistory(855 → 668 行)
2026-02-26 19:59:35 +08:00
kurihada 423b94440d refactor: 拆分 ShareCardModal(951 行 → 4 个模块)
- ShareCardModal.tsx (199 行): 模态框编排逻辑(图片生成、保存、分享)
- RestaurantShareCard.tsx: 餐厅分享卡片纯展示组件
- BlindboxShareCard.tsx: 盲盒分享卡片纯展示组件
- shareImage.ts: 图片工具函数(toPng 封装、dataUrl 转换、下载)
2026-02-26 19:54:12 +08:00
kurihada 07541ed686 refactor: 提取 useGeolocation hook 和 joinRoom 工具函数
- useGeolocation: 将 PanicPage 中 ~50 行 GPS 定位逻辑(requestGps + reverseGeocode + 状态管理)提取为独立 hook
- joinRoom: 统一 3 处重复的 POST /api/room/:id/join 调用(room、invite、panic 页面)
2026-02-26 19:49:05 +08:00
kurihada 0c5676493e refactor: 提取 UserAvatar、Input、Card 三个可复用 UI 组件
- UserAvatar: 统一头像渲染(5 种尺寸),新增 resolveAvatar 工具函数,替换 SwipeDeck 和 RoomManageModal 中的重复逻辑
- Input: 统一表单输入样式(4 种尺寸 + default/purple 变体),替换 AuthModal、ProfilePage、BlindboxPage 共 12 处
- Card: 统一卡片容器样式 + 可选淡入动画和延迟,替换 ProfilePage 中 7 处重复的 motion.div
2026-02-26 19:43:07 +08:00
kurihada 9641acbcbd refactor: 提取 buildNavUrl 工具函数,统一 room API 错误处理
- 将 MatchResult.tsx 和 profile/page.tsx 中重复的导航 URL 构建逻辑提取到 src/lib/navigation.ts
- 7 个 room API 路由从 return NextResponse.json({ error }, { status }) 统一改为 throw ApiError,由 apiHandler 统一捕获
2026-02-26 19:31:00 +08:00
kurihada c9e20d4c95 refactor: 提取 useShare hook,统一分享和剪贴板逻辑
新增 useShare(Web Share API + clipboard fallback),
消除 QrInviteModal、BlindboxRoomPage、ShareCardModal 三处重复的分享代码。
2026-02-26 19:25:44 +08:00
kurihada 1229bb849b refactor: 提取 validation.ts 和 amap.ts,统一 API 路由校验逻辑
新增 validation.ts(用户名/密码/邮箱/内容/房间名/必填字段校验)
和 amap.ts(AMAP API key 校验),消除 7 个路由中的重复验证代码。
2026-02-26 19:22:17 +08:00
kurihada 455b9e04d8 refactor: 提取 Button 组件,统一按钮变体、尺寸和加载状态
新增 Button.tsx 支持 5 种变体(primary/secondary/danger/ghost/purple)、
3 种尺寸(sm/md/lg)、pill/rounded 形状及内置 loading 状态,
替换 8 个文件中 16 处重复的按钮样板代码。
2026-02-26 18:39:14 +08:00
kurihada 19edcaeeb5 refactor: 提取 requireUserId/requireUser/requireMembership 校验工具
- 新增 requireUserId:统一 14 处 userId 非空校验,返回 401
- 新增 requireUser:统一 4 处用户存在性检查,返回 404
- validateMembership 升级为 requireMembership,直接抛 403
- 混合校验拆分为 auth(401) + 字段(400),状态码更准确
2026-02-26 18:17:17 +08:00
kurihada 0595887480 refactor: 引入 apiHandler + ApiError,消除 20 个路由的 try/catch 样板
- 新增 src/lib/api.ts:ApiError 错误类 + apiHandler 统一包装器
- 20 个 API 路由统一使用 apiHandler,删除重复的 try/catch 块
- 验证错误改用 throw new ApiError(),减少嵌套层级
- join/manage 路由的错误码映射改为直接抛出 ApiError
- 删除已无引用的 errorResponse 辅助函数
- 净减 273 行代码
2026-02-26 18:08:47 +08:00
kurihada d4c6da57a1 refactor: Toast 升级为全局 Context,消除 onToast prop 透传
将 useToast 从独立 state hook 改为 Context-based,在 layout 中
挂载 ToastProvider 全局渲染 Toast。QrInviteModal、RoomManageModal、
ShareCardModal 不再需要 onToast prop,直接 useToast() 调用即可。
父组件 TopNav、MatchResult、profile、blindbox 移除了本地 Toast
渲染和 onToast 传递逻辑。
2026-02-26 17:57:34 +08:00
kurihada b98920858c refactor: 提取 useToast hook + Toast 组件,消除 4 处重复的通知逻辑
将 state + setTimeout 自动消失逻辑封装为 useToast hook,
Toast UI 统一为组件支持 top/bottom 两种位置,净减约 12 行。
2026-02-26 17:50:28 +08:00
kurihada 948274bcb9 refactor: 提取 Modal 基础组件,消除 4 个弹窗的重复样板代码
将 backdrop 遮罩、点击关闭、AnimatePresence 动画封装为 Modal 组件,
支持 sheet(底部弹出)和 dialog(居中缩放)两种变体,净减约 110 行。
2026-02-26 17:45:52 +08:00
kurihada ac8cb8c635 feat: 盲盒想法支持编辑和删除,展示用户已投入的想法列表 2026-02-26 16:55:58 +08:00
kurihada b1cbf0d589 fix: 修复页面过渡动画导致 fixed 定位失效和全局徽章被遮挡 2026-02-26 16:49:06 +08:00