Implement RVW V4.0 tenant-aware backend/frontend flow with tenant routing, config APIs, and full portal UX updates. Sync system/RVW/deployment docs to capture verified upload-review-report workflow and next-step admin configuration work. Made-with: Cursor
6.2 KiB
AI智能审稿系统 (期刊SaaS版) 域名架构与多租户落地技术指南
文档受众: 架构师、前端研发、后端研发、运维工程师
文档目的: 明确期刊 SaaS 业务的域名规范,规范单点登录(SSO)及多租户鉴权的开发标准,排查并规避泛域名 SSL 证书与第三方回调等运维/安全隐患。
🏗️ 一、 核心架构决策:三级子域名隔离方案
经过商业定位与工程复杂度的综合评估,系统采用基于功能属性的三级子域名方案:
[tenantId].review.xunzhengyixue.com (例如:jtim.review.xunzhengyixue.com)
为什么选择这个方案?
- 商业心智精准:使用 review 明确了我们是“AI 辅助审稿工具”,而非沉重的全流程投审稿系统,降低客户防备心。
- 品牌与视觉隔离:彻底抛弃 URL路径隔离,为每个期刊提供独立的门户网址。
- 架构解耦:与现有的临床研究主站(.yanjiu. 或主域)及临床试验项目(.iit.)在命名空间上完美平行,为未来的 SaaS 矩阵拓展留足空间。
🛠️ 二、 运维团队 (DevOps) 执行清单
运维团队需在阿里云环境中完成以下配置,该方案无需为每个新增期刊重新发布或修改配置。
1. DNS 云解析配置 (阿里云)
无需每次新增期刊都修改 DNS。请添加一条泛解析记录:
- 主机记录:*.review
- 记录类型:A 或 CNAME
- 记录值:指向 SAE 现有的前端 CLB 负载均衡公网 IP(目前为 8.140.53.236)。
2. ⚠️ 致命避坑:SSL 证书采购与挂载
- 红线提醒:现有的 *.xunzhengyixue.com 泛域名证书绝对无法覆盖四级域名(即无法覆盖 jtim.review.xunzhengyixue.com),直接使用会导致浏览器报红。
- 执行动作:必须重新申请/购买一张专门针对 *.review.xunzhengyixue.com 的泛域名 SSL 证书,并挂载到网关或 CLB 上。
3. Nginx 路由代理配置
前端 frontend-v2 镜像的 nginx.conf 需要配置匹配规则,将所有子域名的请求导向同一个 SPA(单页应用)入口,API 请求依然打向 Node.js 后端。
server {
listen 80;
# 匹配所有的 review 子域名
server_name ~^(?<tenant>.+)\.review\.xunzhengyixue\.com$;
location / {
root /usr/share/nginx/html;
index index.html;
\# 将所有未匹配静态资源的路由回退给 index.html,交由 React Router 处理
try\_files $uri $uri/ /index.html;
}
\# 后端 API 转发保持现有配置不变
location /api/ {
proxy\_pass \[http://172.17.173.73:3001\](http://172.17.173.73:3001);
\# ...
}
}
💻 三、 前端研发 (FE) 执行清单
前端代码库保持同一套,依靠初始化时截取 window.location.hostname 来实现“千刊千面”。
1. 动态截取 TenantID
在应用挂载层(如 App.tsx 或路由初始化钩子中)注入租户识别逻辑:
const host = window.location.hostname; // e.g., jtim.review.xunzhengyixue.com
let currentTenantId = 'default';
// 严格判断当前是否处于期刊审查工作台环境
if (host.endsWith('.review.xunzhengyixue.com')) {
// 提取第一个分段作为 tenantId
currentTenantId = host.split('.')[0];
}
// 拿到 currentTenantId (如 'jtim') 后:
// 1. 发起请求:GET /api/v1/tenants/public-info/jtim 获取 Logo、主题色
// 2. 存入全局状态 (Zustand/Redux),应用定制化 UI
2. 模块级路由屏蔽
利用现有的 moduleRegistry.ts,如果当前 tenantId 为期刊客户,隐藏顶部导航中的 AIA、DC、PKB 模块,仅暴露 RVW 模块视图。
⚙️ 四、 后端研发 (BE) 执行清单
后端核心挑战在于跨域资源共享 (CORS) 以及 防范多租户架构下的数据越权。
1. CORS 动态白名单
禁止写死 Origin,在 Fastify / Express 的 CORS 插件中启用正则匹配:
// 允许主站以及所有的 review 泛域名跨域访问
cors: {
origin: [
/^https?:\/\/([a-zA-Z0-9-]+\.)?xunzhengyixue\.com$/,
/^https?:\/\/([a-zA-Z0-9-]+\.)?review\.xunzhengyixue\.com$/
],
credentials: true
}
2. ⚠️ 致命避坑:SSO 单点登录与防越权 (IDOR 防护)
由于我们采用底层复用 platform_schema.users 的机制,JWT Cookie 会在主域下共享。这带来极大的越权风险。
- SSO 签发:登录时,后端校验用户身份后,必须将当前所在的 tenantId 压入 JWT Payload。
- 双重校验防线 (必须写进中间件 auth.middleware.ts):
当一个请求打到 /api/v2/rvw/tasks 时,中间件必须校验两件事:- Token 解析出的 userId 是否有效。
- 跨域校验:该用户是否有权限访问当前 HTTP 请求 Header 中声明的 x-tenant-id(或者通过请求域名解析出的 tenant)。如果张医生是 iit 的用户,但拿着 Token 试图请求 jtim 租户的数据,必须直接拦截并返回 403 Forbidden。
- ORM 级隔离:所有针对 RVW 模块的 Prisma 查询,必须强制附带 where: { tenantId: request.tenantId },严禁“裸查”。
🔮 五、 未来架构扩展预警:第三方登录限制
【风险说明】
如果您未来计划在 jtim.review... 等域名上引入“微信扫码登录”或微信支付。微信开放平台的安全机制通常要求配置绝对精准的回调域名,往往不支持通配符(*.review...),且回调域名数量有严格上限。
【架构预案:统一认证网关】
为了应对该问题,平台在未来演进时,应规划统一的 Auth Center:
- 任何期刊页面点击“微信登录”,统一 302 Redirect 跳转至主站网关(如 login.xunzhengyixue.com/wechat)。
- 主站统一处理微信回调,生成系统内部的 JWT SSO Token。
- 主站将 Token 携带在参数或安全 Cookie 中,再次 302 Redirect 飞回 jtim.review.xunzhengyixue.com/callback,完成身份同步。
(目前 MVP 阶段账号密码登录不受此限制,仅作未来规划记录)。