跳转至

Agent Portal — 03-28~30 数据重构与移动端优化

关键产品决策

  • Dad 决策:移动端时间选择器改为 wheel picker
  • 手机上时间选择器要改成右侧上下滑动的刻度表(wheel picker)
  • 默认显示最新时间,且刻度居中;不要支持左右滑动
  • 移动端要优化,避免双指放大
  • 原因:传统时间选择器在移动端体验差,需要原生化的滚动体验

  • Dad 决策:服务器聊天与 bot 聊天逻辑分离

  • 服务器聊天与 bot 聊天逻辑不同:引用服务器时,只需把上下文带着一起发给 Ottor direct
  • 不要复用 bot 聊天的复杂链路
  • 原因:服务器状态查询是运维需求,应该直接到负责运维的 bot,而不是走通用聊天路由

  • Dad 决策:Portal 数据源从 Supabase 改为 PostgreSQL 直连

  • 废弃 Supabase 连接方式,统一改为直连 PostgreSQL
  • 所有硬编码 Secret 提取到环境变量
  • 原因:Portal 已是重度数据应用,Supabase 成为多余中间层,直连 PG 更可控

技术问题与根因分析

移动端时间选择器漂移问题

  • 现象:刻度没有对上,选中项漂移
  • 根因:使用了 paddingTop: calc(50% - 32px),但 CSS 的 % padding 是基于宽度而不是高度计算
  • 解决方案
  • 重写 wheel mechanics,去掉依赖 CSS 百分比 padding 的方案
  • 放弃 scroll-snap,改为更可控的 JS-based snap
  • 阻止横向滑动,仅允许纵向滚动
  • 结果:选中项可精确居中,默认定位最新项

服务器聊天报错问题

  • 现象:选中服务器后点击"对话"报 invalid_body_param
  • 根因CommandBar 使用 primaryTarget.user_id = "server-proxy-ottor",但这不是真实的 Mattermost user_id
  • 解决方案:服务器 target 的 user_id 改为 Ottor 的真实 MM user_id
  • 关键原则:聊天目标必须使用真实的用户标识,不能使用虚构的代理标识

PWA 添加到桌面报错

  • 现象:iOS 添加到桌面时报错
  • 根因
  • 浏览器请求 /manifest.json 等 PWA 文件
  • server.cjs(Express)作为 SPA fallback 时找不到这些静态文件
  • 解决方案
  • 增加 manifest.json
  • 确保 public/ 经 Vite build 复制到 dist/
  • 确认 express.static(STATIC_ROOT) 在 catch-all 之前
  • 结论:Express SPA 服务必须保证静态文件优先,fallback 最后

Supabase 接口兼容性问题

  • 现象/pg/query 401,而 REST API 正常
  • 根因:Supabase 的 /pg/rest/v1/ 正常,但 /pg/query endpoint 返回 401
  • Portal 特殊性:39 个 dbQuery 调用,包含 JOIN、聚合、DDL,不适合改成 REST
  • 解决方案
  • 利用 Supabase RPC:exec_sql(执行 DDL)、run_sql(返回结果)
  • 修复 run_sql 对 DML 的支持,改造后支持 SELECT 与 DML

Bot 架构/实现决策

移动端组件架构

  • 实现方案
  • 新增 MobileTimePicker / 重写 InlineTimeMachine
  • 加 safe-area、PWA meta、touch 优化
  • 禁止 pinch-zoom
  • 技术细节
  • iPhone 触感反馈问题:Safari iOS 不支持 navigator.vibrate(),Web 端没有官方 haptic API
  • 处理原则:不能伪造不存在的系统能力,只能通过视觉/滚动反馈模拟

Bot 归档功能设计

  • 业务需求:支持归档 bot 项目,归档后灰色显示、收缩到底部、可展开查看
  • 技术方案:纯前端实现,使用 localStorage 存归档列表
  • 选型理由:归档是用户视图偏好,不是核心业务事实,前端本地存储足够

数据库与前后端分离

  • 前后端分离架构
  • API → 3002
  • 静态前端 → 3013
  • Bearer token middleware 增加到 app.use(express.json())
  • 前端 API 调用统一附带 token
  • 选型理由:职责清晰,便于后续鉴权、SSE、静态资源缓存与独立重启

PostgreSQL 直连方案

  • 架构升级
  • Node 侧:server.cjsdbQuery / dbExec 改为直连 PG
  • Python 侧:新增 push/db.py
  • 新依赖:Node pg、Python psycopg2-binary
  • 迁移策略:引入数据库抽象层,优先直连 PG,保留 Supabase HTTP fallback

里程碑

  1. 移动端体验大幅提升:wheel picker 精确定位,PWA 支持,触控区域优化
  2. 前后端分离完成:Bearer token 中间件就绪,API 与静态资源分离部署
  3. 数据库架构升级:从 Supabase API 迁移到 PostgreSQL 直连
  4. 服务器聊天链路修复:运维指令可直接发给 Ottor,避免复杂路由
  5. 归档功能上线:bot 项目可归档管理,提升界面整洁度
  6. PWA 功能完善:支持添加到桌面,离线友好,移动端体验原生化

关键技术栈

  • 前端:React + Vite + TypeScript
  • 移动端:wheel picker + safe-area + touch optimization
  • 数据库:PostgreSQL 直连 + Supabase fallback
  • 部署:前后端分离 + Bearer token + Docker Compose
  • PWA:manifest.json + offline support

重要经验教训

  • CSS 百分比 padding 基于宽度计算,移动端布局需特别注意
  • Express SPA 服务必须保证静态文件优先级高于 catch-all 路由
  • 聊天目标必须使用真实用户标识,虚构代理标识会导致 API 失败
  • PWA 功能需要完整的静态资源构建链支持

这一阶段确立了 Portal 的现代化移动端体验和稳定的数据库架构,为后续大规模数据处理和复杂交互奠定了基础。