Skip to content

Web 端项目说明书

项目根目录:w:\AI-Pet-Project\xhs-miniapp-saas Web 代码目录:web/ 品牌名:智米口袋 线上地址:https://app.jvguang.com(用户端);API:https://api.jvguang.com(小程序与 Web 共用) 部署文档:web/DEPLOYMENT.mdweb/deploy/nginx-web.conf


1. 项目概述

Web 端是商家(租户)在 PC / 浏览器上使用的内容创作平台,与小程序的业务能力对齐,并额外提供控制台数据看板富文本文章编辑器一键发布到小红书的辅助能力。

核心页面:

路由页面功能
/loginLogin手机号登录/注册(记住我)
//chatChat(智米军师)AI 聊天助手(WebSocket)
/dashboardDashboard(控制台)数据看板(ECharts 图表)
/articlesArticleList(文章管理)文章列表/搜索/删除
/articles/:id/editArticleEditor富文本编辑器(Tiptap)
/article-generateArticleGenerate(文案生成)四步生成向导
/cover-tool/cover-tool/:projectIdCoverTool(封面神器)封面 AI 生成流程
/profileProfile(个人中心)资料/改密/退出
/helpHelp(帮助中心)使用指引/FAQ
*NotFound404

Web 端专属安全模型(与小程序不同):

  • 双 Token 体系:access token 存内存(Zustand,仅 session 内),refresh token 存 HttpOnly CookieSecure; 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 5web/ 目录
UIAnt Design 5 + @ant-design/icons + FontAwesomeConfigProvider 全局主题(智米口袋浅色多色调)
路由react-router-dom v6(createBrowserRouter)+ 懒加载router/index.tsxrouter/guard.tsx
状态Zustand ^4.5(persist 中间件)stores/auth.ts(登录态)、stores/chat.ts(聊天)
图表ECharts 6 + echarts-for-reactDashboard 控制台
富文本Tiptap 2(starter-kit + image + placeholder)ArticleEditor
HTTPaxios ^1.6(拦截器:签名/刷新/错误)api/client.ts
测试Vitest + Testing Library + MSW + jsdomsrc/**/*.test.ts(x)test/
构建Vite 5dev 端口 5175
后端FastAPI /api/*与小程序/Admin 共享
部署Nginx HTTPS + CSPweb/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.tsxReactDOM 挂载 AppApp.tsx入口
src/App.tsx全局配置:antd ConfigProvider智米口袋设计令牌:品牌珊瑚红 #FF2442、数据海军蓝、成功翡翠绿、警示琥珀金、创意紫罗兰;Fraunces 衬线标题字体;全局 antd 组件 token)+ AntdApp + MessageBridge(注册全局 message)+ RouterProviderstyles/tokens.tshooks/useMessage.tsrouter/index.tsx全站主题与布局;改 tokens 影响所有页面配色
index.html页面骨架;标题「智米口袋」SEO 标题
vite.config.tsVite 构建 + vitest 配置 + dev 代理全部构建/测试配置

5.2 路由与守卫

文件作用关联影响
router/index.tsxcreateBrowserRouter 路由表;/loginBlankLayout;其余页面走 RequireAuth + AppLayout;全部懒加载(React.lazy + Suspense);//chat 同页(智米军师);/cover-tool 支持 :projectId 参数layouts/*guard.tsx、全部 pages新增页面在此注册
router/guard.tsxRequireAuth:无 token 重定向 /login;token 存在但 user 缺失时自动 getMe() 补拉用户信息(页面刷新场景)stores/auth.tsapi/auth.ts全站访问控制

5.3 布局

文件作用关联影响
layouts/AppLayout.tsx主框架:桌面端可折叠侧边栏(智米军师/控制台/文章管理/文案生成/封面神器/个人中心),移动端(<lg 断点)改用 Drawer 抽屉侧边栏;顶栏用户头像菜单(退出登录调 apiLogout);内容区 Outletpages/*stores/auth.tsapi/auth.ts全站导航与移动端适配
layouts/BlankLayout.tsx空白布局(登录页专用,无侧边栏)router/index.tsx

5.4 API 层与安全

文件作用关联后端影响
api/client.tsaxios 实例: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.tsWebCrypto HMAC-SHA256:stableStringify 序列化 body → sha256Hexcrypto.subtle.sign('HMAC', token, canonical);canonical=timestamp\nnonce\nMETHOD\npath?query\nbodyHash后端 security/request_signing.py与小程序 miniapp/src/security/signature.ts 算法一致;改动任何一端导致 401
api/auth.tslogin / register / getMe / refresh / logout / changePassword/api/auth/*认证全流程
api/article.tslistArticles / getArticle / updateArticle / deleteArticle/api/article/*文章 CRUD
api/articleGenerate.tssuggestKeywords / generateThemes / generateArticle / checkArticle / adjustArticle/api/article/suggest-keywords/themes/generate/{id}/check/{id}/adjust生成向导
api/task.tsgetTaskStatus / getTaskResult / cancelTask/api/task/*任务轮询
api/upload.tsuploadAnalyze / 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.tsgetWsTicket / listMessages / getSuggestedQuestions / uploadChatMedia / buildWsUrl/api/chat/*聊天 REST + WS
api/signature.test.ts签名算法单元测试(验证与后端一致的关键向量)防止签名回归

5.5 状态管理(Zustand)

文件作用关联影响
stores/auth.ts登录态:token/userpersist 中间件 + 自定义 dualStorage(按「记住我」偏好写 localStorage 或 sessionStorage);仅持久化 token(user 每次登录后重拉);导出非 React getter getAuthToken / clearAuth 供 axios 使用api/client.tsguard.tsxAppLayout登录态生命周期;刷新页面后 token 恢复、user 重拉
stores/chat.ts聊天全状态:WS 连接状态机(ticket/心跳 25s/指数退避重连 ≤5 次)、消息列表(历史+入站)、AI 流式回复状态机(aiReplyStatestreamingContent 临时气泡、aiStatusMessage 状态提示、followupQuestions 追问)、媒体上传状态(pendingMedia)、欢迎语与引导问题(welcomeMessage 不落库)api/chat.tspages/Chat/index.tsxhooks/useChat.ts聊天核心状态;改动影响消息展示与重连逻辑

5.6 Hooks

文件作用关联影响
hooks/useMessage.ts全局 message 桥接:setMessageApi / getMessage / useRegisterMessage(非组件代码如 axios 拦截器用 getMessage() 弹 toast)App.tsxapi/client.ts、各页面全局提示
hooks/useArticle.tsuseArticleList(page,size) 分页列表 hook + useArticle(id) 详情 hook(含加载态)api/article.tsArticleListArticleEditor文章数据加载
hooks/useArticleWizard.ts生成向导状态机(同小程序 useArticleWizard):WizardView、账号类型、MAX_KEYWORDS=8TITLE_MAX_LENGTH=20ArticleGenerate向导逻辑
hooks/useChat.ts聊天逻辑 hook,代理调用 stores/chat.ts 的动作(connect/send/loadHistory 等)stores/chat.tspages/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.tsstores/auth.ts
  • 影响:登录后由后端签发 access token(响应体)并 Set-Cookie refresh_token(HttpOnly)。

Chat/index.tsx — 智米军师(AI 聊天)

  • 功能:聊天 UI:欢迎语卡片(request-welcome WS 消息,不落库)、引导问题(getSuggestedQuestions)、历史消息(游标分页 listMessages)、WS 实时收发(getWsTicketbuildWsUrlWebSocket)、AI 流式回显(ai-reply-start/chunk/done 状态机,临时气泡 → 正式消息)、追问按钮(点击直接发送)、图片/视频上传(uploadChatMedia + 签名 URL 展示)、连接状态标签(ConnectionTag)、输入框 4000 字上限;消息气泡 AI 左、用户右对齐(工程约定)。
  • 关联stores/chat.tshooks/useChat.tsapi/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.tsapi/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_CONFLICTX-Error-Code 头。

ArticleGenerate/index.tsx — 文案生成

  • 功能:四步生成向导(同小程序):方向输入(支持上传素材 AI 分析 → buildDirectionWithUpload 自动填充方向)→ 关键词/角度 → 主题选择 → 生成进度(useTaskPolling)→ 预览/合规自检(check)/AI 微调(adjust)/保存;appendTagsToContent 幂等追加标签。
  • 关联hooks/useArticleWizard.tsuseTaskPolling.tsapi/articleGenerate.tsapi/upload.ts
  • 影响X-Client-Type: web 头使后端标记 source_client=web

CoverTool/index.tsx — 封面神器

  • 功能:封面项目工作台:创建(标题/文案/平台 xiaohongshu|douyin|video_account)、上传素材(≤9 张)、AI 分析生成 4 风格、选风格生成封面(异步轮询 POLL_INTERVAL=3000MAX_WAIT=480000)、选定/下载;移动端适配(BTN_HEIGHT_MOBILE 等)。
  • 关联api/cover.tsapi/upload.ts
  • 影响:封面生成依赖后台「封面 AI 配置」与即梦/Ark 凭据;生成耗时 3-6 分钟。

Profile/index.tsx — 个人中心

  • 功能:用户信息展示、公司资料编辑、修改密码(8-64 位)、退出登录。
  • 关联stores/auth.tsapi/auth.tsapi/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_FONTMONO_FONTCHART_COLORS(ECharts 8 色板)工程约定:所有硬编码颜色必须使用 PALETTE;改令牌全局换肤
utils/stageProgress.ts任务 stage → 进度百分比/阶段文案生成进度

5.9 测试

文件作用
test/setup.tsVitest 环境初始化(jest-dom、MSW mock server)
test/fixtures.ts测试数据夹具(文章/聊天消息等)
stores/auth.test.tsauth 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: DENYStrict-Transport-Security 等安全头
nginx-web.conf.template参数化模板(envsubstWEB_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/refreshWeb 专属:Cookie 换新 access token(限流 10/分钟)
认证POST/api/auth/logoutWeb 专属:撤销 refresh token + 清 Cookie
认证GET/api/auth/me
认证POST/api/auth/password
文章POST/api/article/generate请求头 X-Client-Type: websource_client=web
文章PUT/api/article/{id}Web 专属If-Match: version 乐观锁(409 VERSION_CONFLICT)
文章GET/POST/api/article/suggest-keywordsexpandthemeslist{id}{id}/check{id}/adjust
任务GET/POST`/api/task/{id}/statusresult
上传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_clientvalid_clients = {mp_weixin, mp_xhs, web},Web 请求带 X-Client-Type: web


7. Web 端安全机制详解

  • access token:仅存 Zustand 内存(刷新页面即丢失,通过 refresh 自动恢复),防 XSS 窃取。
  • refresh token:后端 POST /api/auth/loginSet-Cookie(HTTPS 下 Secure; HttpOnly; SameSite=Strict);merchant_refresh_tokens 表存 SHA256 哈希,支持撤销(logout)。
  • 401 流程:client.ts 拦截 → refreshAccessToken() 单例 → Cookie 换新 access → 重放原请求(_retried 防死循环)。
  • 后端 TTL:ACCESS_TOKEN_TTL_HOURS=2REFRESH_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: DENYX-Content-Type-Options: nosniffStrict-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 换新)→ 重放原请求 → 成功继续 / 失败清登录态跳 /login

8.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、验证清单、回滚)。

生产 .envweb/.env.production): VITE_API_BASE=https://api.jvguang.com

后端 .env 关键项(Web 相关):

  • CORS_ORIGINShttps://app.jvguang.com
  • ACCESS_TOKEN_TTL_HOURS=2REFRESH_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. 常见问题

  1. 页面刷新后跳登录:access token 在内存、刷新丢失属预期;此时应自动走 refresh(Cookie)恢复会话——若 refresh 失败(Cookie 丢失/过期)才跳登录页。
  2. 保存文章报「文章已被另一端修改」:双端同时编辑触发乐观锁 409;刷新文章后重试。
  3. 请求报 401「缺少请求签名」:检查 client.ts 拦截器是否在 token 存在时注入签名头。
  4. 聊天连接反复断开:检查 Nginx /api/chat/ws 的 Upgrade/Connection 头(jvguang_web.conf 已有配置);心跳 25s 防代理超时;重连最多 5 次。
  5. CORS 报错:后端 CORS_ORIGINS 需包含 https://app.jvguang.com(开发环境为 http://localhost:5175)。

基于 VitePress 构建 · 由 GitHub Actions 自动部署