前端架构

状态: 2026-06-06 verify(基于 studio-website-monorepo/apps/web 实查)

怎么读:

  • 没 context:Layer 1(30 秒)
  • 改前端代码:Layer 1 + Layer 2(5 分钟)
  • debug 数据流 / 认证:Layer 3
  • 改部署 / 加环境:Layer 4
  • 找 file:line:页面 / Query hook / Pinia 速查表

图例:Mental Model 配色统一 — 紫 = 外部 / 蓝 = 前端节点 / 绿 = 后端 / 黄 = 认证横切 / 灰 = 观测横切


Layer 1 — Mental Model(30 秒)

前端是什么?跟谁通信?

Mental Model — Vue 3 SPA 部署在 Cloudflare Pages,所有数据走 studio-api,认证走 Cognito,观测走 Sentry

一句话:前端是纯 Vue 3 SPA。Cloudflare Pages 只托管静态文件,不跑业务逻辑;所有数据请求走 studio-api(AWS Lambda + Hono);认证走 Cognito + JWT。


Layer 2 — 五层架构(5 分钟)

代码怎么分层?数据从上到下怎么流?

五层架构 — 应用入口 / 页面层 / 组件层 / 逻辑层 / API 层

各层职责速查

路径数量角色
1. 应用入口src/main.ts / src/plugins/ / src/router/7 plugins · 5 guards启动 app + 路由守卫链(dateRedirect → datePreservation → commonGuard → authGuard → analytics)
2. 页面层src/pages/174unplugin-vue-router 文件路由 + 7 nav 分组(Operational + Settings + Auth + 系统页)
3. 组件层src/components/391业务组件 + shadcn-vue 原子组件
4. 逻辑层src/composables/queries/ + src/stores/38 hook + 10 storeTanStack Query 缓存 + Pinia 状态
5. API 层src/api/15 ot-api 文件 + Hono RPC + Cognito AmplifyAxios 拦截器(401 自动刷新 token,最多 3 次)

Layer 3 — 数据流时序 + 认证

用户访问 Dashboard 这一刻发生什么?

数据流时序 — 浏览器 / 路由守卫 / 页面 / TanStack Query / Axios / studio-api 6 个 actor

关键边界:

  • 缓存命中 = 0 网络请求,直接从 TanStack Query 返回
  • 缓存未命中 = Axios 自动加 Authorization: Bearer {token} → studio-api → 写入缓存 → Vue 反应式更新
  • 401 自动刷新 = Axios 响应拦截器最多重试 3 次
  • 认证: AWS Cognito 通过 aws-amplify;JWT 存 localStorage(auth store)绕过 pinia-plugin-persistedstate

Layer 4 — 部署

前端怎么部署到 3 个环境?

部署 — GitHub Actions 3 workflow → wrangler pages deploy → 3 个 CF Pages project / 3 个域名

关键事实(从 .github/workflows/ + apps/web/wrangler.toml verify):

  • 纯 Wrangler CLI 部署,不走 CF Pages git integration
  • 3 个 workflow: deploy-test.yml(push 到 main 自动) / deploy-preprod.yml(workflow_dispatch + push release/pre)/ deploy-prod.yml(workflow_dispatch 手动 + 输 "prod" 二次确认)
  • 3 个 CF Pages project: studio-test / studio-pre / studio
  • 3 个域名: studio-test.retaintive.ai / studio-pre.retaintive.ai / studio.retaintive.ai
  • build 命令: bun run buildwrangler pages deploy dist --project-name={env}

页面速查(Layer 3a)

改某个页面 / 找页面文件位置时查。基于 find src/pages -name "*.vue" 实查共 174 文件。

认证页面(无侧边栏,blank 布局)

路由菜单名功能
/auth/sign-in · /auth/sign-in-2-Cognito 邮箱密码登录(2 个 variant 待 cleanup)
/auth/sign-up-新用户注册
/auth/otp · /auth/forgot-password-OTP + 忘记密码流程
/auth/ringcentral-callback · /auth/callback · /auth/auth-callback-RingCentral / Cognito OAuth 回调

侧边栏 Operational 分组(default 布局)

路由菜单名功能
/dashboardDashboard指标卡、图表、排行榜、转化漏斗
/activity-timelineActivity Timeline店铺活动时间线周视图(原 doc 说 /store-activity,实际路径是 /activity-timeline)
/staff-timelineStaff Timeline员工维度通话详情 + 周排班视图
/callsCall Table通话分页数据表 + 桌面端并排详情面板
/call/:telephonySessionId(子页面)UI Manifest 驱动的动态布局、音频播放、文本同步
/messagesMessagesSMS / 语音邮件 / 传真,按对话分组
/tasksTasksLead pipeline、任务 CRUD、import、通信日志(16 sub-components)
/calendarCalendar店铺排班日历(9 sub-components,逐步替代 /store-schedule)
/lead-trackerLead Tracker线索列表、结果编辑、通话关联
/phone-activity按号码展示通话/SMS/语音邮件/线索历史

侧边栏 Settings 分组(default 布局)

路由菜单名功能
/stores · /stores-managementStores & GroupsRingCentral 连接、电话号码分配(2 个路径待统一)
/store-scheduleStore Schedule店铺周时间表(被 /calendar 替代中)
/access-managementAccess Management团队成员角色分配
/settings · /settings/connections · /settings/change-passwordSettings账号设置多页面
/connectionsConnections顶层 connections 页(区别于 /settings/connections)
/billing · /billing-history · /billing-plan · /transactionsBilling计费、历史、交易
/appsApps应用卡片 / 集成

系统页面 + Disabled / Cleanup

路由功能
/errors/401 · /403 · /404 · /500 · /503错误页
/legalTerms / Privacy 占位
/dev/call-detail-test开发测试 fixture

Disabled / WIP: src/pages/users/index.vue.disabled(与 /access-management 可能重叠)、src/pages/help-center.vue.disabled(stub)。Cleanup 候选: activity-timeline-old/lead-tracker-old/ 目录仍存在,未启用。


Query Hook 速查(Layer 3b)

改 cache strategy / 加新 hook / debug 缓存失效时查。src/composables/queries/ 共 38 个 hook 文件(2026-05-29 ls 实查)。另有多个非 query composable 分散在 src/composables/ 根,处理 UI 状态(如 useCalendaruseDashboardMetricsuseStaffPerformance),不走 TanStack 缓存。

Composable缓存时间用途
useCalls(storeId, dateRange)3 分钟通话列表
useCallDetail(sessionId)30 天通话详情(不可变)
useRecentCalls()最近通话
useLinkedCall()关联通话
useAudioUrl()录音预签 URL
useLeads(params)5 分钟线索列表
useConversations(params)10 分钟对话列表(匹配旧 conversationsCache TTL)
useConversationStatus()2 分钟对话状态(同文件)
useMessagesStore()消息 store 查询
usePhoneNumbers()5 分钟电话号码列表
usePhoneActivity()按号码历史
useStores()5 分钟店铺列表
useTypedStores()类型化店铺查询
useOrganizations()组织列表(多租户)
useConnections()5 分钟RC 连接状态
useOAuth()OAuth 状态
useWebhook()(即旧 useWebhookStatus)30 秒Webhook 实时状态
useBlackouts()禁呼时段
useOperatingHours()营业时间
useTasks()任务列表
useDashboardAnalytics()Dashboard 分析数据
useStaffTimelineAnalytics()员工时间线分析

("—" = 该 hook 未显式设置 staleTime,使用 TanStack Query 默认值 0。)


Pinia Store 速查(Layer 3c)

改全局状态 / debug 多 tab 持久化时查。pinia-plugin-persistedstate 默认 storage 在 src/plugins/pinia/index.ts:9 设为 sessionStorage(tab 生命周期内持久)。只有 auth store 直接用 localStorage.* API 手动持久,绕开了 plugin。

Store持久化关键状态
authlocalStorage(手动 localStorage.setItem)idToken、accessToken、user、memberships
storesstores[]、currentStoreId
organizationsessionStorage(persist: true)当前 org、多租户切换(核心多租户开关)
ringcentralsessionStorage(手动 sessionStorage.setItem)isConnected、phoneNumbers、OAuth state
date-rangestartDate、endDate(URL 已是持久载体)
theme (system-config)sessionStorage(persist: true)radius: number + theme: Theme(8 种色名枚举 zinc / red / rose / orange / green / blue / yellow / violet)
access-managementsessionStorageAccess management 页状态
analytics-dashboardsessionStorageDashboard 筛选、tab 状态
calls-tablesessionStorageCall table 列配置、排序
lead-tracker-tablesessionStorageLead tracker 列配置、排序

观测(横切)

  • Sentry @sentry/vue: error tracking;src/utils/sentry.ts(env detection + user context);disabled on localhost
  • 自建 Session Analytics: src/utils/sentry-analytics.ts 定义 SessionAnalytics + FeatureCategory enum(DASHBOARD / CALL_ANALYSIS / FILTERS / EXPORTS …);上报到 Sentry breadcrumb / context
  • 不是每个按钮都 log — 只 log feature usage(用了哪个功能 / 切了哪个 store / 导出了什么)+ error path

Sentry 接入 / metric 上报细节: 见 system-design/observability.md(待新建,跟踪后续 reorg)


技术栈速查

层级技术
框架Vue 3.5 + TypeScript
构建Vite 8 (beta)(rolldown-ready;当前仍是 Rollup,未 swap 到 rolldown-vite)
样式Tailwind CSS v4 + shadcn-vue + Reka UI
路由Vue Router + unplugin-vue-router(文件路由)+ vite-plugin-vue-layouts(blank / default / analysis 布局)
状态Pinia + pinia-plugin-persistedstate(默认 sessionStorage)
数据获取TanStack Query + Axios + Hono RPC(typed API 试点)
图表ECharts + vue-echarts + Unovis
表单Vee-Validate + Zod
国际化vue-i18n 11.x
认证AWS Amplify(Cognito)
图标Lucide Vue
动画motion-v + AutoAnimate
工具VueUse、Day.js
共享类型@retaintive/common ^0.54.0(callytics-infrastructure 用 "*" = latest)
测试Vitest + Vue Test Utils + Puppeteer + Lighthouse(perf budget)
代码健康Oxlint + Knip(未使用依赖扫描)
部署Cloudflare Pages(Wrangler),通过 @sentry/vite-plugin 在 prod build 上传 sourcemap
监控Sentry + Google Analytics 4

Legacy

Legacy

retaintive/studio-web repo github.com/retaintive/studio-web 是 pre-monorepo 版本的前端仓库,已不再活跃(最后 commit 2026-03-04)。所有新工作走 studio-website-monorepo/apps/web。如果误落到 studio-web,请与团队确认是否待 archive。