Web 端项目说明书
项目根目录:
w:\AI-Pet-Project\xhs-miniapp-saasWeb 代码目录:web/品牌名:智米口袋 线上地址:https://app.jvguang.com(用户端);API:https://api.jvguang.com(小程序与 Web 共用) 部署文档:web/DEPLOYMENT.md、web/deploy/nginx-web.conf
1. 项目概述
Web 端是商家(租户)在 PC / 浏览器上使用的内容创作平台,与小程序的业务能力对齐,并额外提供控制台数据看板、富文本文章编辑器与一键发布到小红书的辅助能力。
核心页面:
| 路由 | 页面 | 功能 |
|---|---|---|
/login | Login | 手机号登录/注册(记住我) |
/、/chat | Chat(智米军师) | AI 聊天助手(WebSocket) |
/dashboard | Dashboard(控制台) | 数据看板(ECharts 图表) |
/articles | ArticleList(文章管理) | 文章列表/搜索/删除 |
/articles/:id/edit | ArticleEditor | 富文本编辑器(Tiptap) |
/article-generate | ArticleGenerate(文案生成) | 四步生成向导 |
/cover-tool、/cover-tool/:projectId | CoverTool(封面神器) | 封面 AI 生成流程 |
/profile | Profile(个人中心) | 资料/改密/退出 |
/help | Help(帮助中心) | 使用指引/FAQ |
* | NotFound | 404 |
Web 端专属安全模型(与小程序不同):
- 双 Token 体系:access token 存内存(Zustand,仅 session 内),refresh token 存 HttpOnly Cookie(
Secure; SameSite=Strict),/api/auth/refresh自动续期;比小程序端的单 token 更安全。 - HMAC-SHA256 请求签名:用 WebCrypto(
crypto.subtle)实现,与小程序纯 JS 实现算法完全一致(后端统一验签)。 - 请求统一带
X-Client-Type: web,后端据此给文章标记source_client=web(与小程序生成的文章可区分来源)。 - 文章更新使用 乐观锁(
If-Match: version),防止 Web 与小程序双端同时编辑互相覆盖(409 版本冲突)。
2. 技术栈
| 层次 | 技术 | 说明 |
|---|---|---|
| 框架 | React 18 + TypeScript 5 | web/ 目录 |
| UI | Ant Design 5 + @ant-design/icons + FontAwesome | ConfigProvider 全局主题(智米口袋浅色多色调) |
| 路由 | react-router-dom v6(createBrowserRouter)+ 懒加载 | router/index.tsx、router/guard.tsx |
| 状态 | Zustand ^4.5(persist 中间件) | stores/auth.ts(登录态)、stores/chat.ts(聊天) |
| 图表 | ECharts 6 + echarts-for-react | Dashboard 控制台 |
| 富文本 | Tiptap 2(starter-kit + image + placeholder) | ArticleEditor |
| HTTP | axios ^1.6(拦截器:签名/刷新/错误) | api/client.ts |
| 测试 | Vitest + Testing Library + MSW + jsdom | src/**/*.test.ts(x)、test/ |
| 构建 | Vite 5 | dev 端口 5175 |
| 后端 | FastAPI /api/* | 与小程序/Admin 共享 |
| 部署 | Nginx HTTPS + CSP | web/deploy/nginx-web.conf(.template) |
3. 整体架构图
mermaid
flowchart TB
classDef fe fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1
classDef gw fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,color:#4a148c
classDef db fill:#fff8e1,stroke:#ef6c00,stroke-width:2px,color:#e65100
subgraph WEB["Web 端 React SPA"]
direction TB
ROUTER["router/index.tsx + guard.tsx"]
LAYOUT["AppLayout / BlankLayout"]
PAGES["Login / Chat / Dashboard / ArticleList<br/>ArticleEditor / ArticleGenerate / CoverTool<br/>Profile / Help / NotFound"]
HOOKS["useArticle / useChat / useArticleWizard<br/>useTaskPolling / useGenerateProgress / useMessage"]
STORES["stores/auth.ts (内存token)<br/>stores/chat.ts (WS + 消息状态机)"]
API["api/* client.ts axios<br/>signature.ts WebCrypto 签名<br/>refresh.ts 单例刷新"]
end
subgraph BACKEND["FastAPI :8000"]
direction TB
VERIFY["verify_request_signature<br/>HMAC + nonce"]
AUTH["/api/auth/* 登录/refresh/me/password"]
ARTICLE["/api/article/* + If-Match 乐观锁"]
TASK["/api/task/*"]
COVER["/api/cover/*"]
CHAT["/api/chat/* + /api/chat/ws"]
UPLOAD["/api/upload/*"]
WS["/api/chat/ws"]
RLS["DbSession (app.current_merchant_id)"]
end
subgraph DATABASE["PostgreSQL + pgvector"]
direction TB
DB["merchants/articles/tasks/images/knowledge<br/>cover_*/chat_*/platform_rules ..."]
end
ROUTER --> LAYOUT
LAYOUT --> PAGES
PAGES --> HOOKS
PAGES --> STORES
HOOKS --> API
STORES --> API
API -->|"Authorization + X-Signature + X-Client-Type:web"| VERIFY
VERIFY --> AUTH
VERIFY --> ARTICLE
VERIFY --> TASK
VERIFY --> COVER
VERIFY --> CHAT
VERIFY --> UPLOAD
CHAT -->|"wss"| WS
AUTH --> RLS
ARTICLE --> RLS
TASK --> RLS
COVER --> RLS
UPLOAD --> RLS
RLS --> DB
class ROUTER,LAYOUT,PAGES,HOOKS,STORES,API fe
class VERIFY,AUTH,ARTICLE,TASK,COVER,CHAT,UPLOAD,WS,RLS gw
class DB db
style WEB fill:#e3f2fd,stroke:#1565c0,stroke-width:1px
style BACKEND fill:#ffffff,stroke:#6a1b9a,stroke-width:1px
style DATABASE fill:#fff3e0,stroke:#ef6c00,stroke-width:1px图例:🟦 蓝 = Web 前端 · 🟪 紫 = FastAPI 后端(白底) · 🟧 橙 = PostgreSQL 数据库
4. 目录结构总览
web/
├── DEPLOYMENT.md # 部署文档(域名/HTTPS/CSP/验证清单)
├── index.html # Vite 入口
├── package.json / package-lock.json
├── tsconfig.json / tsconfig.node.json
├── vite.config.ts # dev 5175 + 代理 + vitest 配置
├── web-dist.tar.gz # 已构建产物包
├── deploy/
│ ├── nginx-web.conf # 生产 Nginx(HTTPS + CSP + 安全头)
│ ├── nginx-web.conf.template # 模板(envsubst 参数化)
│ ├── nginx-web-8080.conf # 阶段1历史配置(已弃用)
│ └── jvguang_web.conf # 线上 Web + WS 配置
└── src/
├── main.tsx # React 挂载
├── App.tsx # ConfigProvider 主题 + RouterProvider + MessageBridge
├── vite-env.d.ts
├── api/
│ ├── client.ts # axios 实例 + 签名 + 401 自动刷新
│ ├── refresh.ts # 单例 refresh 工具(防循环依赖)
│ ├── signature.ts # WebCrypto HMAC-SHA256 签名
│ ├── auth.ts # 登录/注册/me/refresh/logout/改密
│ ├── article.ts # 文章 CRUD
│ ├── articleGenerate.ts # 生成/关键词/主题/check/adjust
│ ├── task.ts # 任务状态/结果/取消
│ ├── upload.ts # 素材上传分析
│ ├── cover.ts # 封面项目全部接口
│ ├── chat.ts # 聊天 REST + WS URL
│ └── signature.test.ts # 签名算法单测
├── components/
│ └── PublishToXhsModal.tsx # 一键发布到小红书(复制+跳转引导)
├── hooks/
│ ├── useMessage.ts # antd message 桥接
│ ├── useArticle.ts # 文章列表/详情数据 hook
│ ├── useArticleWizard.ts # 生成向导状态机
│ ├── useChat.ts # 聊天逻辑 hook(代理到 store)
│ ├── useGenerateProgress.ts# 生成进度映射
│ └── useTaskPolling.ts # 任务轮询
├── layouts/
│ ├── AppLayout.tsx # 主布局(侧边栏+顶栏+移动端 Drawer)
│ └── BlankLayout.tsx # 空白布局(登录页用)
├── pages/
│ ├── Login/index.tsx
│ ├── Chat/index.tsx
│ ├── Dashboard/index.tsx
│ ├── ArticleList/index.tsx
│ ├── ArticleEditor/index.tsx
│ ├── ArticleGenerate/index.tsx
│ ├── CoverTool/index.tsx
│ ├── Profile/index.tsx
│ ├── Help/index.tsx
│ └── NotFound/index.tsx
├── router/
│ ├── index.tsx # 路由表(懒加载)
│ └── guard.tsx # RequireAuth 登录守卫
├── stores/
│ ├── auth.ts # 登录态(token+user,persist 记住我)
│ ├── auth.test.ts # auth store 单测
│ └── chat.ts # 聊天状态(WS/消息/流式/媒体)
├── styles/
│ └── tokens.ts # 设计令牌(PALETTE/字体/ECharts 色板)
├── test/
│ ├── fixtures.ts # 测试夹具
│ └── setup.ts # 测试环境初始化(MSW/jsdom)
├── types/
│ └── api.ts # 接口 TS 类型
├── utils/
│ └── stageProgress.ts # 阶段→进度映射
└── ArticleList.test.tsx # 文章列表页组件测试5. 文件详解
5.1 入口与主题
| 文件 | 作用 | 关联 | 影响 |
|---|---|---|---|
src/main.tsx | ReactDOM 挂载 App | App.tsx | 入口 |
src/App.tsx | 全局配置:antd ConfigProvider(智米口袋设计令牌:品牌珊瑚红 #FF2442、数据海军蓝、成功翡翠绿、警示琥珀金、创意紫罗兰;Fraunces 衬线标题字体;全局 antd 组件 token)+ AntdApp + MessageBridge(注册全局 message)+ RouterProvider | styles/tokens.ts、hooks/useMessage.ts、router/index.tsx | 全站主题与布局;改 tokens 影响所有页面配色 |
index.html | 页面骨架;标题「智米口袋」 | — | SEO 标题 |
vite.config.ts | Vite 构建 + vitest 配置 + dev 代理 | 全部 | 构建/测试配置 |
5.2 路由与守卫
| 文件 | 作用 | 关联 | 影响 |
|---|---|---|---|
router/index.tsx | createBrowserRouter 路由表;/login 走 BlankLayout;其余页面走 RequireAuth + AppLayout;全部懒加载(React.lazy + Suspense);/ 与 /chat 同页(智米军师);/cover-tool 支持 :projectId 参数 | layouts/*、guard.tsx、全部 pages | 新增页面在此注册 |
router/guard.tsx | RequireAuth:无 token 重定向 /login;token 存在但 user 缺失时自动 getMe() 补拉用户信息(页面刷新场景) | stores/auth.ts、api/auth.ts | 全站访问控制 |
5.3 布局
| 文件 | 作用 | 关联 | 影响 |
|---|---|---|---|
layouts/AppLayout.tsx | 主框架:桌面端可折叠侧边栏(智米军师/控制台/文章管理/文案生成/封面神器/个人中心),移动端(<lg 断点)改用 Drawer 抽屉侧边栏;顶栏用户头像菜单(退出登录调 apiLogout);内容区 Outlet | pages/*、stores/auth.ts、api/auth.ts | 全站导航与移动端适配 |
layouts/BlankLayout.tsx | 空白布局(登录页专用,无侧边栏) | router/index.tsx | — |
5.4 API 层与安全
| 文件 | 作用 | 关联后端 | 影响 |
|---|---|---|---|
api/client.ts | axios 实例:baseURL=VITE_API_BASE、60s 超时(AI 180s)、withCredentials:true(携带 refresh cookie);请求拦截:注入 Authorization + WebCrypto 签名头(含 query 的完整 URL)+ X-Client-Type: web;响应拦截:401 自动 refresh 一次并重放原请求(refresh.ts 单例),refresh 失败跳登录;非 409 错误统一 toast(409 版本冲突交调用方处理) | 后端全部接口 | 全局请求行为与安全边界 |
api/refresh.ts | 单例刷新:并发 401 共享同一 _refreshPromise,成功后 login(newToken),失败 logout();独立文件避免 client↔auth 循环依赖 | POST /api/auth/refresh | 双 token 续期核心 |
api/signature.ts | WebCrypto HMAC-SHA256:stableStringify 序列化 body → sha256Hex → crypto.subtle.sign('HMAC', token, canonical);canonical=timestamp\nnonce\nMETHOD\npath?query\nbodyHash | 后端 security/request_signing.py | 与小程序 miniapp/src/security/signature.ts 算法一致;改动任何一端导致 401 |
api/auth.ts | login / register / getMe / refresh / logout / changePassword | /api/auth/* | 认证全流程 |
api/article.ts | listArticles / getArticle / updateArticle / deleteArticle | /api/article/* | 文章 CRUD |
api/articleGenerate.ts | suggestKeywords / generateThemes / generateArticle / checkArticle / adjustArticle | /api/article/suggest-keywords、/themes、/generate、/{id}/check、/{id}/adjust | 生成向导 |
api/task.ts | getTaskStatus / getTaskResult / cancelTask | /api/task/* | 任务轮询 |
api/upload.ts | uploadAnalyze / uploadImagesAnalyze(File→FormData / base64 JSON) | /api/upload/analyze、/analyze-images | 素材 AI 分析 |
api/cover.ts | 封面全部接口(create/list/get/update/delete/upload/analyze/styles/covers/select) | /api/cover/* | 封面神器 |
api/chat.ts | getWsTicket / listMessages / getSuggestedQuestions / uploadChatMedia / buildWsUrl | /api/chat/* | 聊天 REST + WS |
api/signature.test.ts | 签名算法单元测试(验证与后端一致的关键向量) | — | 防止签名回归 |
5.5 状态管理(Zustand)
| 文件 | 作用 | 关联 | 影响 |
|---|---|---|---|
stores/auth.ts | 登录态:token/user;persist 中间件 + 自定义 dualStorage(按「记住我」偏好写 localStorage 或 sessionStorage);仅持久化 token(user 每次登录后重拉);导出非 React getter getAuthToken / clearAuth 供 axios 使用 | api/client.ts、guard.tsx、AppLayout | 登录态生命周期;刷新页面后 token 恢复、user 重拉 |
stores/chat.ts | 聊天全状态:WS 连接状态机(ticket/心跳 25s/指数退避重连 ≤5 次)、消息列表(历史+入站)、AI 流式回复状态机(aiReplyState、streamingContent 临时气泡、aiStatusMessage 状态提示、followupQuestions 追问)、媒体上传状态(pendingMedia)、欢迎语与引导问题(welcomeMessage 不落库) | api/chat.ts、pages/Chat/index.tsx、hooks/useChat.ts | 聊天核心状态;改动影响消息展示与重连逻辑 |
5.6 Hooks
| 文件 | 作用 | 关联 | 影响 |
|---|---|---|---|
hooks/useMessage.ts | 全局 message 桥接:setMessageApi / getMessage / useRegisterMessage(非组件代码如 axios 拦截器用 getMessage() 弹 toast) | App.tsx、api/client.ts、各页面 | 全局提示 |
hooks/useArticle.ts | useArticleList(page,size) 分页列表 hook + useArticle(id) 详情 hook(含加载态) | api/article.ts、ArticleList、ArticleEditor | 文章数据加载 |
hooks/useArticleWizard.ts | 生成向导状态机(同小程序 useArticleWizard):WizardView、账号类型、MAX_KEYWORDS=8、TITLE_MAX_LENGTH=20 | ArticleGenerate | 向导逻辑 |
hooks/useChat.ts | 聊天逻辑 hook,代理调用 stores/chat.ts 的动作(connect/send/loadHistory 等) | stores/chat.ts、pages/Chat | 聊天页面数据流 |
hooks/useGenerateProgress.ts | 任务阶段→进度百分比/文案 | useTaskPolling | 进度展示 |
hooks/useTaskPolling.ts | 轮询任务状态(间隔可配),done 取结果、failed 报错、cancelled 停止 | api/task.ts | 生成闭环 |
5.7 页面详解
Login/index.tsx — 登录/注册
- 功能:手机号+密码登录/注册/「记住我」;登录成功
stores/auth.login(token)→ 跳转来源页或/。 - 关联:
api/auth.ts、stores/auth.ts。 - 影响:登录后由后端签发 access token(响应体)并 Set-Cookie refresh_token(HttpOnly)。
Chat/index.tsx — 智米军师(AI 聊天)
- 功能:聊天 UI:欢迎语卡片(
request-welcomeWS 消息,不落库)、引导问题(getSuggestedQuestions)、历史消息(游标分页listMessages)、WS 实时收发(getWsTicket→buildWsUrl→WebSocket)、AI 流式回显(ai-reply-start/chunk/done状态机,临时气泡 → 正式消息)、追问按钮(点击直接发送)、图片/视频上传(uploadChatMedia+ 签名 URL 展示)、连接状态标签(ConnectionTag)、输入框 4000 字上限;消息气泡 AI 左、用户右对齐(工程约定)。 - 关联:
stores/chat.ts、hooks/useChat.ts、api/chat.ts。 - 影响:与小程序聊天页共用同一后端会话与记忆;客服从 Admin 端接管时此处实时收到
sender_role=admin消息。
Dashboard/index.tsx — 控制台
- 功能:数据看板:KPI 卡片(
mockKpis)、趋势折线(mockTrend)、分布饼图(mockDistribution)、来源雷达(mockSourceRadar)、活跃动态(mockActivities);ECharts 图表组件(build*Option构建 option,ECHARTS_OPTS={renderer:'svg', notMerge:true, lazyUpdate:true})。 - 工程约定:option 用 useMemo 稳定引用 + 禁用动画,避免 ECharts 因 option 引用变化自动重渲染;外层容器
width:'100%'自适应。 - 关联:
styles/tokens.ts(CHART_COLORS)。 - 影响:当前为演示数据(mock),后续接后端统计接口时替换
mock*函数即可。
ArticleList/index.tsx — 文章管理
- 功能:文章列表(分页)、按主题/来源筛选、删除(确认弹窗);点击行进入编辑器。
- 关联:
hooks/useArticle.ts、api/article.ts。 - 影响:展示小程序与 Web 双端生成的文章(
source_client字段区分来源)。
ArticleEditor/index.tsx — 富文本编辑器
- 功能:Tiptap 富文本编辑文章(标题+正文+标签);保存带
If-Match: version乐观锁,版本冲突(409)提示「文章已被另一端修改,请刷新后重试」;衬线展示字体DISPLAY_FONT。 - 关联:
api/article.ts(updateArticle 传If-Match)、hooks/useArticle.ts。 - 影响:乐观锁是双端防覆盖的关键;版本不匹配时后端返回
VERSION_CONFLICT且X-Error-Code头。
ArticleGenerate/index.tsx — 文案生成
- 功能:四步生成向导(同小程序):方向输入(支持上传素材 AI 分析 →
buildDirectionWithUpload自动填充方向)→ 关键词/角度 → 主题选择 → 生成进度(useTaskPolling)→ 预览/合规自检(check)/AI 微调(adjust)/保存;appendTagsToContent幂等追加标签。 - 关联:
hooks/useArticleWizard.ts、useTaskPolling.ts、api/articleGenerate.ts、api/upload.ts。 - 影响:
X-Client-Type: web头使后端标记source_client=web。
CoverTool/index.tsx — 封面神器
- 功能:封面项目工作台:创建(标题/文案/平台 xiaohongshu|douyin|video_account)、上传素材(≤9 张)、AI 分析生成 4 风格、选风格生成封面(异步轮询
POLL_INTERVAL=3000、MAX_WAIT=480000)、选定/下载;移动端适配(BTN_HEIGHT_MOBILE等)。 - 关联:
api/cover.ts、api/upload.ts。 - 影响:封面生成依赖后台「封面 AI 配置」与即梦/Ark 凭据;生成耗时 3-6 分钟。
Profile/index.tsx — 个人中心
- 功能:用户信息展示、公司资料编辑、修改密码(8-64 位)、退出登录。
- 关联:
stores/auth.ts、api/auth.ts、api/merchant(公司资料)。 - 影响:改密后旧 token 仍有效至过期(当前实现不撤销 access token)。
Help/index.tsx — 帮助中心
- 功能:模块使用指引(
MODULES)+ 常见问题(FAQS)静态内容。 - 关联:无后端依赖。
- 影响:纯静态。
NotFound/index.tsx — 404
- 功能:未知路由兜底页。
- 关联:
router/index.tsx的*路由。
5.8 设计令牌与工具
| 文件 | 作用 | 影响 |
|---|---|---|
styles/tokens.ts | 设计令牌唯一来源:PALETTE(品牌/信息/成功/警示/创意 5 语义色 + 中性色)、DISPLAY_FONT(Fraunces 衬线)、BODY_FONT、MONO_FONT、CHART_COLORS(ECharts 8 色板) | 工程约定:所有硬编码颜色必须使用 PALETTE;改令牌全局换肤 |
utils/stageProgress.ts | 任务 stage → 进度百分比/阶段文案 | 生成进度 |
5.9 测试
| 文件 | 作用 |
|---|---|
test/setup.ts | Vitest 环境初始化(jest-dom、MSW mock server) |
test/fixtures.ts | 测试数据夹具(文章/聊天消息等) |
stores/auth.test.ts | auth store 行为单测(token 存取/记住我) |
api/signature.test.ts | 签名算法单测 |
ArticleList.test.tsx | 文章列表页组件测试(列表渲染/交互) |
5.10 部署相关(web/deploy/)
| 文件 | 作用 |
|---|---|
nginx-web.conf | 生产 Nginx:HTTPS、静态托管 dist、/api/ 反代 127.0.0.1:8000、/api/chat/ws WebSocket 升级头、严格 CSP 头、X-Frame-Options: DENY、Strict-Transport-Security 等安全头 |
nginx-web.conf.template | 参数化模板(envsubst:WEB_DOMAIN/API_DOMAIN/API_UPSTREAM/CSP_HEADER) |
jvguang_web.conf | 线上实际生效配置(含 WS location) |
nginx-web-8080.conf | 阶段 1 历史配置(已弃用) |
DEPLOYMENT.md | 部署全流程 + 验证清单 + 回滚方案 |
6. 后端关联接口总表(Web 使用)
Web 端使用接口 = 小程序接口全集(除小程序专属的 uni 相关)+ Web 专属差异:
| 模块 | 方法 | 路径 | Web 差异点 |
|---|---|---|---|
| 认证 | POST | /api/auth/login | 响应外额外 Set-Cookie refresh_token(HttpOnly) |
| 认证 | POST | /api/auth/register | 同上 |
| 认证 | POST | /api/auth/refresh | Web 专属:Cookie 换新 access token(限流 10/分钟) |
| 认证 | POST | /api/auth/logout | Web 专属:撤销 refresh token + 清 Cookie |
| 认证 | GET | /api/auth/me | — |
| 认证 | POST | /api/auth/password | — |
| 文章 | POST | /api/article/generate | 请求头 X-Client-Type: web → source_client=web |
| 文章 | PUT | /api/article/{id} | Web 专属:If-Match: version 乐观锁(409 VERSION_CONFLICT) |
| 文章 | GET/POST | /api/article/suggest-keywords、expand、themes、list、{id}、{id}/check、{id}/adjust | — |
| 任务 | GET/POST | `/api/task/{id}/status | result |
| 上传 | POST | /api/upload/analyze、/analyze-images | — |
| 封面 | 全部 | /api/cover/* | — |
| 聊天 | POST/GET | /api/chat/ws-ticket、/messages、/suggested-questions、/upload、/uploads/* | — |
| 聊天 | WS | /api/chat/ws | 浏览器原生 WebSocket(wss://) |
后端判定
source_client:valid_clients = {mp_weixin, mp_xhs, web},Web 请求带X-Client-Type: web。
7. Web 端安全机制详解
7.1 双 Token(Access in Memory + Refresh in HttpOnly Cookie)
- access token:仅存 Zustand 内存(刷新页面即丢失,通过 refresh 自动恢复),防 XSS 窃取。
- refresh token:后端
POST /api/auth/login时Set-Cookie(HTTPS 下Secure; HttpOnly; SameSite=Strict);merchant_refresh_tokens表存 SHA256 哈希,支持撤销(logout)。 - 401 流程:
client.ts拦截 →refreshAccessToken()单例 → Cookie 换新 access → 重放原请求(_retried防死循环)。 - 后端 TTL:
ACCESS_TOKEN_TTL_HOURS=2、REFRESH_TOKEN_TTL_DAYS=7。
7.2 HMAC 请求签名(WebCrypto)
- 与小程序
signature.ts同算法,但用crypto.subtle(异步、更高效);签名 key = access token;后端 nonce 防重放(TTL 300s)+ 时间窗校验。
7.3 CSP 与安全头(Nginx)
- 严格 CSP(
default-src 'self'; script-src 'self'; connect-src 'self' https://api.jvguang.com; frame-ancestors 'none'...);X-Frame-Options: DENY;X-Content-Type-Options: nosniff;Strict-Transport-Security;后台接口X-Robots-Tag: noindex。
7.4 乐观锁
articles.version字段;PUT 带If-Match,版本不匹配返回 409(X-Error-Code: VERSION_CONFLICT),前端提示刷新,防止 Web/小程序双端覆盖。
8. 关键业务流程
8.1 登录与自动续期
Login → POST /api/auth/login(含限流) → 后端 bcrypt 校验 + 签发 access + Set-Cookie refresh
→ stores/auth.login(access)(内存)→ guard 拉取 getMe() → 进入应用
→ 任意请求 401 → refreshAccessToken()(Cookie 换新)→ 重放原请求 → 成功继续 / 失败清登录态跳 /login8.2 文章生成(与小程序一致,但标记 web 来源)
ArticleGenerate 向导 → generateArticle(X-Client-Type: web)→ task_id
→ useTaskPolling 轮询 → 后端 LangGraph(rag_search→agent_generate→format_assemble)→ 落库 source_client=web
→ 预览 → check/adjust → 保存(If-Match 乐观锁)→ ArticleList/Editor 可编辑8.3 聊天(WebSocket)
Chat 页 → getWsTicket(30s 票据)→ buildWsUrl → new WebSocket(`wss://api.jvguang.com/api/chat/ws?ticket=`)
→ stores/chat.ts 状态机:连接/心跳(25s)/断线重连(退避≤5次)
→ send dm → 后端回显 + AI 流式回复(ai-reply-start/chunk/done)→ streamingContent 临时气泡 → 正式消息 + 追问
→ Admin 客服接管时接收 sender_role=admin 消息8.4 控制台图表
- 当前为本地 mock 数据(
mockKpis/mockTrend/...),ECharts option 用useMemo稳定 +notMerge关闭重渲染;后续接后端/api/admin/stats或新统计接口时替换 mock。
9. 部署
bash
cd web
npm run build # tsc && vite build → dist/
sudo cp -r dist/* /var/www/web/
sudo cp deploy/nginx-web.conf /etc/nginx/sites-available/web && sudo ln -sf ... && sudo nginx -t && sudo systemctl reload nginx详见 web/DEPLOYMENT.md(证书、CSP、验证清单、回滚)。
生产 .env(web/.env.production): VITE_API_BASE=https://api.jvguang.com。
后端 .env 关键项(Web 相关):
CORS_ORIGINS含https://app.jvguang.comACCESS_TOKEN_TTL_HOURS=2、REFRESH_TOKEN_TTL_DAYS=7- HTTPS 下 refresh Cookie 自动
Secure; HttpOnly; SameSite=Strict
10. 影响分析汇总
| 变更点 | 影响范围 | 风险 |
|---|---|---|
修改 signature.ts / 签名逻辑 | Web 全部接口 401 | 高 |
修改 client.ts 拦截器(刷新/重放/toast) | 全站请求行为 | 高 |
修改 stores/chat.ts(WS 状态机) | 聊天页收发/重连/流式 | 中 |
修改 styles/tokens.ts PALETTE | 全站配色(App.tsx 主题 + 各页面) | 中 |
修改 router/guard | 页面访问控制 | 中 |
修改 Dashboard ECharts option 稳定性 | 图表重渲染性能 | 低 |
| 后端文章流水线 / 聊天 / 封面 AI 配置变更 | 三端(Web+小程序+Admin)同时受影响 | 高 |
| 后端 auth(双 token / 签名 / 限流)变更 | 三端登录与安全边界 | 高 |
11. 常见问题
- 页面刷新后跳登录:access token 在内存、刷新丢失属预期;此时应自动走 refresh(Cookie)恢复会话——若 refresh 失败(Cookie 丢失/过期)才跳登录页。
- 保存文章报「文章已被另一端修改」:双端同时编辑触发乐观锁 409;刷新文章后重试。
- 请求报 401「缺少请求签名」:检查
client.ts拦截器是否在 token 存在时注入签名头。 - 聊天连接反复断开:检查 Nginx
/api/chat/ws的 Upgrade/Connection 头(jvguang_web.conf已有配置);心跳 25s 防代理超时;重连最多 5 次。 - CORS 报错:后端
CORS_ORIGINS需包含https://app.jvguang.com(开发环境为http://localhost:5175)。