feat: add Feishu user authorization flow documentation and update frontend guidelines

- Added new documentation for the Feishu user authorization flow, detailing the backend protocol, authorization initiation, callback handling, and token management.
- Updated frontend index to link to the new authorization flow documentation for better accessibility and guidance on UI design consistency.
This commit is contained in:
yuxuanhui
2026-08-31 09:18:03 +08:00
parent 3d838866cd
commit dcd6d44960
15 changed files with 3817 additions and 732 deletions
@@ -0,0 +1,130 @@
---
id: 20260805-request-package-multi-instance-dev-backend
title: 统一 request 包并按目标系统创建多个请求实例
created: 2026-08-05
updated: 2026-08-05
status: candidate
scope: global
category: frontend-architecture
confidence: medium
last_verified: 2026-08-05
promotion_target: pattern
projects:
- oppein-knowledge
tags:
- request-package
- http-client
- multi-instance
- oauth2
- vite-proxy
- environment-config
---
# 统一 request 包并按目标系统创建多个请求实例
## Trigger
前端项目同时调用多个后端系统,且不同接口需要不同的 `baseURL`、请求头、认证令牌或开发代理路径;尤其是本地开发时,业务服务可能需要访问本地后端,而登录、用户、菜单等接口仍需要访问外部网关。
## Context
本次欧派学习平台前端改造中,项目引入统一的 `@oppein-react/request` 包,替换业务侧分散的请求封装。项目存在两类请求目标:
1. 当前项目后端接口,例如课程、讲师、资料和题库接口。
2. 欧派外部网关接口,例如 OAuth 登录、退出、当前用户、菜单和用户中心接口。
本地开发还存在两种运行方式:
- 当前项目后端运行在 `http://abc.oppein.com:8080`;
- 当前项目后端通过 UAT 网关的 `https://apigatewayuat.oppein.com/knowledge` 路由访问。
如果所有请求共用一个客户端,容易把外部网关接口发到项目后端,或者把项目后端接口发到网关;如果把环境变量的上游目标地址直接当作浏览器请求地址,又会绕过 Vite proxy,产生跨域或本地端口不可达问题。
## Evidence
- `assp-knowledge-frontend/src/utils/request-client.ts:3-9`:统一从 `@oppein-react/request` 引入 `createRequest`、错误类型和请求类型。
- `assp-knowledge-frontend/src/utils/request-client.ts:100-105`:`knowledgeRequestClient` 使用当前项目后端请求基址,并按本地后端目标决定是否注入本地 JWT。
- `assp-knowledge-frontend/src/utils/request-client.ts:123-145`:分别暴露 `knowledgeRequest` 和 `gatewayRequest` 两个请求实例;前者服务当前项目后端,后者服务欧派外部网关。
- `assp-knowledge-frontend/src/utils/request-client.ts:59-80`:固定应用头、`Oauth2-AccessToken` 和本地 `Oauth2-Jwt` 由动态 Header provider 统一生成,业务 API 不再重复拼装。
- `assp-knowledge-frontend/src/common/config.ts:23-34`:把环境变量提供的上游目标地址与浏览器实际请求基址分开;开发环境分别使用 `/knowledge` 和同源空基址进入 Vite proxy。
- `assp-knowledge-frontend/vite.config.ts:29-38`:`/knowledge` proxy 根据目标地址是否包含路径前缀决定 rewrite;本地 8080 去掉 `/knowledge`,网关目标保留 `/knowledge`。
- `assp-knowledge-frontend/vite.config.ts:90-98`:当前项目后端统一使用一个 `/knowledge` proxy,外部网关仅保留 `/oauth`、`/ucenterapi`、`/user` 三类入口。
- `assp-knowledge-frontend/.env.example:3-36`:已记录本地 8080、UAT 网关和生产网关的配置差异,明确网关模式的 `VITE_API_BASE_URL` 必须包含 `/knowledge`。
- 2026-08-05:通过 TypeScript 检查、UAT/生产构建、`git diff --check` 和临时 mock upstream proxy smoke test 验证请求实例与 rewrite 方案;本地目标收到 `/auth/currentUser`,网关目标保留 `/oauth/logout`、`/ucenterapi/*` 和 `/user/*` 原始路径。
- 2026-08-05:实际 UAT 路由对比验证:`https://apigatewayuat.oppein.com/course/list` 和不带前缀的讲师接口返回 `404`,带 `/knowledge` 的对应接口返回 `400`,证明网关路由前缀必须保留;`400` 来自缺少真实认证或业务参数,不代表路由不存在。
## Root cause
已验证:请求客户端的职责不只是发送 HTTP,还携带目标系统的认证和路径契约。当前项目后端与外部网关的请求头、令牌来源、错误处理和 URL 入口不同,因此必须使用多个由同一 `request` 包创建的实例,而不是在业务 API 中手写不同的 `fetch` 或修改单个全局客户端。
已验证:开发环境需要把“上游目标地址”和“浏览器请求地址”分离。浏览器只能请求同源的 `/knowledge`、`/oauth`、`/ucenterapi`、`/user`,Vite proxy 再根据 `VITE_API_BASE_URL` 和 `VITE_EXTERNAL_API_BASE_URL` 转发到本地后端或网关。
已验证:网关路由前缀是目标地址的一部分。`VITE_API_BASE_URL=https://apigatewayuat.oppein.com` 会使 `/knowledge/course/list` 被 rewrite 成 `/course/list`,从而命中网关 `404`;正确的网关配置必须是 `https://apigatewayuat.oppein.com/knowledge`。
推断:以后遇到“一个前端项目在开发态切换本地后端与远程网关”的项目,优先设计多个语义明确的 request 实例,并让 proxy 根据目标 URL 的 pathname 处理前缀,比为每个业务接口复制代理规则更稳定。
## Preferred action
1. 先按目标系统划分请求实例,而不是按页面或接口数量划分:
- `knowledgeRequest`:当前项目后端;
- `gatewayRequest`:外部 OAuth、用户、菜单和权限网关。
2. 所有实例都通过统一 `request` 包创建,复用请求方法、错误类型、响应处理和动态 Header provider。
3. 把认证差异放在实例配置或 Header provider 中:
- 网关实例统一注入 `AppCode`、`SubAppCode`、`X-Requested-With` 和 `Oauth2-AccessToken`;
- 本地项目后端按需注入 `Oauth2-Jwt`;
- 业务 API 不重复手写认证头。
4. 在配置层保留两组概念:
- `apiBaseUrl` / `externalApiBaseUrl`:环境变量提供的上游目标地址;
- `apiRequestBaseUrl` / `externalRequestBaseUrl`:浏览器实际请求基址。
5. 开发环境始终优先使用同源相对路径:
- 当前项目后端使用 `/knowledge`;
- 外部网关使用 `/oauth`、`/ucenterapi`、`/user`。
6. 统一 proxy 时,让目标 URL 的 pathname 决定是否追加或保留服务前缀:
- 本地 `http://abc.oppein.com:8080`:`/knowledge/foo` → `/foo`;
- 网关 `https://apigatewayuat.oppein.com/knowledge`:`/knowledge/foo` → `/knowledge/foo`。
7. `.env.example` 必须同时给出本地后端和网关模式,并明确“本地目标不带 `/knowledge`、网关目标必须带 `/knowledge`”。
8. 验证时至少分别检查一个项目后端接口和一个外部网关接口的最终上游路径,不要只检查浏览器看到的相对 URL。
## Boundaries
- 这是“多个目标系统共享同一 request 包”的模式,不意味着所有项目都必须命名为 `knowledgeRequest` 和 `gatewayRequest`。
- 如果所有接口确实属于同一个后端系统,单一 request 实例更简单;不要为了形式创建多个实例。
- `VITE_EXTERNAL_API_BASE_URL` 的具体网关路径取决于网关契约;本条只约束当前项目后端的 `/knowledge` 路由必须与目标地址保持一致。
- 代理只能解决开发环境的同源转发,不能替代生产环境的网关路由、鉴权配置或 CORS 策略。
- 没有真实登录态时,网关接口返回 `400`、`401` 或业务错误不能单独证明代理失败;应先区分“路由是否命中”和“鉴权/业务参数是否有效”。
## Failed approaches
- 用一个 request 实例承载当前项目后端和外部网关:请求可能命中错误服务,且认证头边界不清晰。
- 在每个业务 API 中直接写 `fetch`、`axios` 或手工拼接 `Oauth2-AccessToken`:会绕过统一错误处理和动态令牌注入,后续修改难以收敛。
- 把 `VITE_API_BASE_URL=https://apigatewayuat.oppein.com` 当作 UAT 项目后端配置:Vite 会把 `/knowledge/course/list` rewrite 成 `/course/list`,网关返回 `404`。
- 把绝对上游地址直接作为开发环境浏览器 base URL:会绕过 Vite proxy,导致跨域或无法访问本地端口。
- 为 `/material`、`/course`、`/lecturer` 等每个业务前缀分别复制 proxy:本地后端和网关环境的路径规则容易逐条漂移,新增接口还要重复改配置。
## Examples
```ts
const knowledgeRequestClient = createRequest({
baseURL: apiRequestBaseUrl,
headers: createKnowledgeHeaders,
});
const gatewayRequestClient = createRequest({
baseURL: externalRequestBaseUrl,
headers: createGatewayHeaders,
});
```
```env
# 本地后端
VITE_API_BASE_URL=http://abc.oppein.com:8080
VITE_EXTERNAL_API_BASE_URL=https://apigatewayuat.oppein.com
# 或:UAT 网关
VITE_API_BASE_URL=https://apigatewayuat.oppein.com/knowledge
VITE_EXTERNAL_API_BASE_URL=https://apigatewayuat.oppein.com
```
## Promotion record
- Not promoted. 当前先作为候选经验保存在中央知识库;如果后续在另一个项目或第二次独立改造中复用并验证,再考虑提升为通用 pattern、模板或自动检查。