# RVW V4.0 智能审稿 SaaS 开发计划 > **文档版本:** v1.1 > **制定日期:** 2026-03-14 > **最后更新:** 2026-03-14(架构审查报告修正 × 6) > **产品版本:** RVW V4.0 (Journal AI Review SaaS) > **当前基线:** RVW V3.0.2(方法学分治并行 + 20检查点 + 展示收敛) > **目标状态:** 多租户期刊 SaaS 平台,支持路径路由隔离、SSO统一账号、ADMIN配置中枢、Handlebars报告渲染、退修信 SSE 流式生成 > > **⚠️ v1.1 架构前提变更(来自架构审查报告):** > MVP 阶段放弃子域名方案(个人证书不支持泛域名),改用 **URL 路径隔离**:`review.xunzhengyixue.com/jtim`。10 本期刊以内路径方案完全够用,超过 10 本后再迁移子域名架构。 --- ## 一、升级背景与核心目标 ### 1.1 为什么要升级 当前 V3.0.2 是面向"内部临床研究人员"的单租户审稿工具,已具备强大的 4 维审查能力(稿约规范性 / 方法学 / 数据验证 / 临床评估)。 **痛点:** 1. **品牌混杂**:不同期刊编辑(JTIM、CMJ等)登录的是同一套通用界面,无法体现期刊个性 2. **标准无弹性**:每个期刊有自己独特的稿约要求,但系统只有一套固定规则 3. **缺乏完整工作流**:没有"人工复核确认 → 勾选缺陷 → 导出退修信"的编辑侧工作闭环 ### 1.2 核心目标(PRD 定义的 4 个目标) | 目标 | 说明 | |------|------| | **多租户隔离** | 每个期刊作为独立租户,拥有独立子域名(`jtim.review.xunzhengyixue.com`)、独立品牌登录页、数据硬隔离 | | **完整期刊工作流** | 作者投递预审 → 责编AI审查 → 人工复核勾选 → 导出内部报告/退修信 | | **ADMIN 配置中枢** | 不另建后台,直接扩展现有 ADMIN 端的 TenantDetailPage,实现"千刊千面"配置 | | **SSO 统一通行证** | 底层复用 `platform_schema.users`,同一账号可在主站与各期刊子域名间无缝漫游 | ### 1.3 MVP 策略 **Managed SaaS(代运营/配置)模式**:复杂配置由内部运营团队通过 ADMIN 端完成,期刊客户只需"开箱即用"业务端。这意味着 Phase 1-2 是内部工具建设,Phase 3 才是对外交付。 --- ## 二、整体架构变更一览 ``` V3.0.2 现状 (单租户) V4.0 目标 (多租户 SaaS) — 路径隔离方案 ──────────────────── ────────────────────────────────────── 单一登录页 review.xunzhengyixue.com/jtim (JTIM 专属) ↓ review.xunzhengyixue.com/cmj (CMJ 专属) 单一 ReviewTask 表 review.xunzhengyixue.com/... (更多期刊) ↓ ↓ 固定 Skills 配置 前端路由提取 :tenantId → x-tenant-id Header ↓ ↓ 固定 Prompt 内容 JWT Payload 注入 tenantId + 中间件双重校验 ↓ ↓ 固定 Word 导出格式 tenant_rvw_configs 表 (per-tenant 配置) ↓ SkillExecutor 按租户动态装配 ├── 静态系统协议 (RVW_xxx_SYSTEM_BASE) └── 动态业务层 (tenant_rvw_configs) ↓ Handlebars 渲染 expert_report_markdown (ADMIN 端支持模板实时预览) ↓ 双轨导出: 内部审稿报告 + 作者退修信 (SSE 流式) ``` --- ## 三、三阶段里程碑 ``` Phase 1 (1周) Phase 2 (1.5周) Phase 3 (2周) 多租户数据基础 ADMIN配置中枢 期刊业务端重构 ────────────── ───────────────── ────────────────────── DB Schema 扩展 → TenantDetailPage扩展 → 动态品牌登录页 JWT 注入 tenantId RVW Config CRUD API 稿件管理池 Dashboard 子域名路由检测 Skills 引擎按租户装配 沉浸式 4-Tab 审稿详情 CORS 动态白名单 Handlebars 渲染引擎 人工复核 (HitL) 确认 DevOps SSL证书 Zod Schema 校验 退修信一键生成 Prompt 动静分离落地 双轨导出 (报告+退修信) ``` **总工期预估:4~4.5 周** --- ## 四、Phase 1:多租户数据隔离与账号体系(预计 1 周) ### 4.1 数据库改造 > ⚠️ **【审查报告修正 P0】历史数据迁移两步走**(来自架构审查报告第 1 条): > 生产环境中已存在大量 V3.0 历史审稿任务。直接添加 `tenantId` 非空外键约束,Prisma Migrate 会因历史数据为空而报错中断。 > **必须严格按以下两步执行:** > 1. 先在 `platform_schema.tenants` 中初始化"默认临床主站租户"(如 `tenant-default-yanjiu`) > 2. 在 Prisma migration SQL 中,先 `ALTER TABLE ADD COLUMN tenant_id TEXT NULL`,再 `UPDATE review_tasks SET tenant_id = 'tenant-default-yanjiu' WHERE tenant_id IS NULL`,最后 `ALTER TABLE ALTER COLUMN tenant_id SET NOT NULL` **任务 1.1:新增 `tenant_rvw_configs` 扩展表** ```sql -- Prisma Schema 新增 model TenantRvwConfig { id String @id @default(uuid()) tenantId String @unique tenant Tenant @relation(fields: [tenantId], references: [id]) -- 稿约规范评估配置 editorialRules Json? -- JSONB: 规则数组 + fatal 标记 -- 方法学评估配置 methodologyExpertPrompt String? -- Text: 专家的业务评判标准 methodologyHandlebarsTemplate String? -- Text: 报告渲染模板 -- 数据验证配置 dataForensicsLevel String @default("L2") -- L1/L2/L3 -- 临床评估配置 finerWeights Json? -- JSONB: {feasibility, innovation, ethics, relevance, novelty} clinicalExpertPrompt String? -- Text: 临床评估补充要求 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@schema("platform_schema") } ``` **执行方式:** `prisma migrate dev --name add_tenant_rvw_configs` **任务 1.2:`review_tasks` 表添加 `tenantId` 字段** 当前 `review_tasks` 表未关联租户。需要: ```sql ALTER TABLE rvw_schema.review_tasks ADD COLUMN tenant_id TEXT REFERENCES platform_schema.tenants(id); CREATE INDEX idx_review_tasks_tenant_id ON rvw_schema.review_tasks(tenant_id); ``` 执行方式:`prisma migrate dev --name add_tenant_id_to_review_tasks` --- ### 4.2 SSO 鉴权体系升级 **任务 1.3:JWT 身份信息(⚠️ 架构修正:JWT 不含 tenantId)** > ⚠️ **【Sprint 审查修正 #1】JWT 中不应写入 tenantId** > 若 JWT 中写死 `tenantId: 'jtim'`,由于主站与期刊门户共用同一个 `localStorage`,用户在新标签页打开 `review.../cmj` 时,系统会携带含 `jtim` 的 Token,造成跨租户漫游验证失败。 > > **正确的 SSO 统一通行证架构:** > - JWT Payload 中**仅包含** `userId`(个人身份,不含业务 tenantId) > - 中间件(任务 1.4)完全通过 `x-tenant-id` Header 获取租户上下文 > - 后端实时查询 `tenant_members` 表核验 `userId` 是否属于该租户 > - 优势:同一个 Token 可以在多期刊间无缝漫游,零冲突 修改 `backend/src/modules/auth/auth.service.ts`: - JWT Payload 格式:`{ userId, role, iat, exp }`(移除 tenantId 字段) - 登录接口本身不感知 tenantId(登录是个人行为,与租户无关) **任务 1.4:中间件双重校验防线** 修改 `backend/src/modules/auth/auth.middleware.ts`: > ⚠️ **【审查报告修正 P1-2】URL Slug → tenantId 映射** > 前端传来的 `x-tenant-id: jtim` 是 `tenants.code` 字段(业务缩写),而 Prisma 查询需要 `tenants.id`。 > 查阅现有 Schema 确认:`tenants` 表已有 `code String @unique` 字段,`id` 为手动维护的字符串主键。 > > **中间件处理逻辑(必须严格遵循):** > 1. 从 Header 中提取 `tenantSlug = request.headers['x-tenant-id']`(即 `code` 值,如 `'jtim'`) > 2. 查询 `tenant = await prisma.tenants.findUnique({ where: { code: tenantSlug } })` > **用 Redis 或内存 LRU 缓存此查询**,TTL 5 分钟,避免每次请求都打 DB > 3. 若查不到 → 返回 404(租户不存在) > 4. 将 `tenant.id` 挂载到 `request.tenantId`,将完整 `tenant` 对象挂载到 `request.tenant` > 5. **跨域校验**:校验该 `userId` 是否为 `tenant_members` 表中该租户的成员 > 若不是 → 拒绝 403 Forbidden(内部运营人员持 `ops:user-ops` 权限除外) **任务 1.5:Prisma 全局查询守护** 在 RVW 模块所有 Prisma 查询中强制附带 `where: { tenantId: request.tenantId }`,杜绝"裸查"。 --- ### 4.3 前端路径路由(已调整为路径隔离方案) > ⚠️ **【审查报告修正 P2】tenantId 提取方式变更**(来自架构审查报告第 6 条): > 路径方案下,后端无法从 `Host` 或 `Origin` 隐式推断租户,必须由前端显式携带 `x-tenant-id` Header。 **任务 1.6:前端 Tenant 识别逻辑** 修改 `frontend-v2/src/App.tsx`(或路由初始化): ```typescript // MVP 路径隔离方案:review.xunzhengyixue.com/:tenantSlug const pathname = window.location.pathname; const match = pathname.match(/^\/([a-zA-Z0-9-]+)/); const tenantSlug = (match && match[1] !== 'api') ? match[1] : 'default'; // 存入 Zustand store(tenantSlug 字段) useTenantStore.setState({ tenantSlug }); ``` > ⚠️ **【审查报告修正 P2-3】严禁使用 `axiosInstance.defaults.headers` 全局赋值** > SPA 中 Axios 实例是全局单例,用 `defaults.headers` 写死后,用户从 `/jtim` 返回主站 `/` 或切换至 `/cmj` 时,旧 Header 不会自动清除,导致"在主站发出了携带 JTIM Header 的请求"状态污染。 > **必须使用请求拦截器动态读取:** ```typescript // 在 axiosInstance 初始化时注册一次,此后永远动态读取当前租户 axiosInstance.interceptors.request.use(config => { const { tenantSlug } = useTenantStore.getState(); if (tenantSlug && tenantSlug !== 'default') { config.headers['x-tenant-id'] = tenantSlug; } return config; }); ``` **任务 1.6b:Zustand 租户 Store + 路由观察者 + RouteGuard 修正(✅ 已完成)** > **解决的三个问题(对应 Sprint 审查 #3 React 状态陷阱):** > 1. SPA 内部跳转(`` 导航)不刷新页面,一次性初始化代码不重新执行 > 2. Axios `defaults.headers` 全局写死导致租户状态污染 > 3. RouteGuard 将主平台业务路径(`/rvw`、`/ai-qa`)误判为租户 slug **新增文件:** `frontend-v2/src/framework/tenant/useTenantStore.ts` — Zustand 全局租户 store: ```typescript export const useTenantStore = create((set) => ({ tenantSlug: null, setTenantSlug: (slug) => set({ tenantSlug: slug }), })) ``` `frontend-v2/src/framework/tenant/useTenantObserver.ts` — 路由感知 hook(使用 `useLocation()`): ```typescript // RESERVED_PATHS = 静态保留词 + MODULES 中所有注册路径首段(自动维护) export function useTenantObserver() { const location = useLocation() const setTenantSlug = useTenantStore(s => s.setTenantSlug) useEffect(() => { setTenantSlug(extractTenantSlug(location.pathname)) }, [location.pathname, setTenantSlug]) } ``` **修改文件 `App.tsx`:** - 在 `` 内挂载 ``(调用 `useTenantObserver()`) - Axios 拦截器改为实时读取 store,防止状态污染: ```typescript apiClient.interceptors.request.use((config) => { const { tenantSlug } = useTenantStore.getState() if (tenantSlug) config.headers['x-tenant-id'] = tenantSlug return config }) ``` **修改文件 `RouteGuard.tsx`:** - 复用 `extractTenantSlug`(自动排除所有注册模块路径,防止 `/rvw` 误判) - 未登录跳转对齐 App.tsx 现有路由 `/t/:tenantCode/login`: ```typescript // /jtim/dashboard 未登录 → /t/jtim/login?redirect=%2Fjtim%2Fdashboard // /rvw/tasks 未登录 → /login(行为不变) function buildLoginRedirect(pathname: string): string { const tenantSlug = extractTenantSlug(pathname) if (!tenantSlug) return '/login' return `/t/${tenantSlug}/login?redirect=${encodeURIComponent(pathname)}` } ``` **任务 1.6b:路由守卫租户感知重定向(✅ 已完成)** > **问题根因**:`RouteGuard.tsx` 原来写死 ``,访问 `/jtim/*` 未登录时会跳到 `/login`,丢失 `jtim` 标识,登录页无法加载期刊品牌 UI。 已修改 `frontend-v2/src/framework/router/RouteGuard.tsx`: ```typescript // 新增两个辅助函数: /** 不属于期刊租户的保留路径前缀 */ const RESERVED_PATHS = new Set(['login', 'admin', 'org', 'api', '']) /** 提取第一段路径为 tenantSlug,保留字返回 null */ function extractTenantSlug(pathname: string): string | null { const firstSegment = pathname.split('/').filter(Boolean)[0] ?? '' return RESERVED_PATHS.has(firstSegment) ? null : firstSegment } /** 构造携带租户标识的登录跳转目标 */ function buildLoginRedirect(pathname: string): string { const tenantSlug = extractTenantSlug(pathname) if (!tenantSlug) return '/login' // 主站保持原有行为 const redirectParam = encodeURIComponent(pathname) return `/login?tenant=${tenantSlug}&redirect=${redirectParam}` } // 未登录跳转(原来): // 未登录跳转(现在): ``` **效果**: - 访问 `/jtim/dashboard` 未登录 → 跳转 `/login?tenant=jtim&redirect=%2Fjtim%2Fdashboard` - 访问 `/login`、`/admin` 等主站路径 → 仍跳转 `/login`(行为不变) - 登录页可通过 `tenant` query param 加载 JTIM 品牌 UI,登录后可通过 `redirect` param 跳回原页面 **任务 1.7:模块动态过滤** 利用现有 `moduleRegistry.ts`:若当前 `tenantId` 为期刊租户,隐藏 AIA/DC/PKB 等非 RVW 模块入口。 --- ### 4.4 DevOps 配置(路径方案简化版) > ⚠️ **【审查报告修正 P1】前端打包 base 路径 + Nginx SPA 回退**(来自架构审查报告第 3 条): > 路径隔离方案下,若前端打包使用相对路径(`./`),访问 `/jtim` 时浏览器会错误请求 `/jtim/assets/...`,导致白屏。 > 且用户在 `/jtim/dashboard` 直接刷新时,Nginx 找不到物理目录会报 404。 > **修正:** Vite `vite.config.ts` 中设置 `base: '/'`;Nginx 必须配置 `try_files $uri $uri/ /index.html;` **任务 1.8:Nginx + Vite 配置(运维 + 前端)** | 项 | 内容 | 负责人 | |----|------|--------| | **DNS** | 无需泛解析,沿用现有 `review.xunzhengyixue.com` A 记录即可 | 运维 | | **SSL 证书** | 沿用现有 `*.xunzhengyixue.com` 证书,无需额外申购 | 运维 | | **Nginx 配置** | 确保 SPA 路由回退:`try_files $uri $uri/ /index.html;` | 运维 | | **Vite 打包** | `vite.config.ts` 中 `base: '/'`(绝对根路径,不可用相对路径)| 前端 | | **CORS 白名单** | 后端 Fastify CORS 配置中增加 `review.xunzhengyixue.com` | 后端 | --- ### 4.5 Phase 1 验收标准 - [ ] `tenant_rvw_configs` 表成功创建并迁移 - [ ] 历史 `review_tasks` 的 `tenant_id` 已批量刷新为默认租户 ID,无数据丢失 - [ ] 访问 `review.xunzhengyixue.com/jtim` 可正常加载前端(不白屏、不 404) - [ ] 在 `/jtim/dashboard` 直接刷新,页面正常加载(Nginx SPA 回退生效) - [ ] 使用主站账号登录,JWT 中包含 `tenantId: 'jtim'`,Axios 自动携带 `x-tenant-id` Header - [ ] 用 JTIM 账号请求 CMJ 数据,后端返回 403 - [ ] 前端侧边栏在期刊租户下隐藏非 RVW 模块 --- ## 五、Phase 2:ADMIN 运营端配置中枢扩展(预计 1.5 周) ### 5.1 后端 API 新增 **任务 2.1:RVW Config CRUD 接口** 新文件:`backend/src/modules/admin/rvw-config/` - `rvwConfigController.ts` - `rvwConfigService.ts` - `rvwConfigRoutes.ts` 接口规范: ``` GET /api/admin/tenants/:id/rvw-config 获取租户审稿配置 PUT /api/admin/tenants/:id/rvw-config 更新(UPSERT)租户审稿配置 ``` 权限:复用现有 `ops:user-ops` 中间件,仅内部运营人员可访问。 --- ### 5.2 前端 ADMIN 扩展 **任务 2.2:TenantDetailPage 新增"智能审稿配置" Tab** 修改 `frontend-v2/src/modules/admin/pages/TenantDetailPage.tsx`: Tab 内包含 4 个子配置面板: **Panel A — 稿约规范评估配置** - 规则列表(可增删改):每条规则含 `code / description / fatal(是否一票否决)` - 示例:`摘要字数 > 250` → fatal ✓ **Panel B — 方法学评估配置** - 文本区:业务评判标准(方法学专家语言,可自由编辑,不需懂JSON) - 文本区:Handlebars 报告展示模板(提供默认模板,可覆盖) - 变量说明:`{{system_metrics.overall_score}}`, `{{#each system_metrics.checkpoints}}` 等 - > ⚠️ **【审查报告修正 P1】必须增加"测试渲染"预览**(来自架构审查报告第 5 条): > 若无预览功能,运营人员写错 Handlebars 语法后需上传真实 PDF 等待 5 分钟才能发现报错,试错成本极高。 > **复用 ADMIN 现有的"测试渲染 (Test Render)"能力**:提供一份 Mock 好的 `system_metrics` JSON 假数据,点击"预览"按钮即可在右侧即时看到渲染效果,语法正确后再保存。 **Panel C — 数据验证配置** - 下拉选择验证深度:`L1 算术验证` / `L2 统计验证` / `L3 双通道核查` **Panel D — 临床评估配置** - 5 个 FINER 权重输入框(总和校验 100%) - 文本区:专科特色补充要求 --- ### 5.3 引擎改造(核心) **任务 2.3:SkillExecutor 按租户动态装配** 修改 `backend/src/modules/rvw/skills/core/executor.ts`(或 `reviewWorker.ts`): - 在执行每个 Skill 前,通过 `tenantId` 查询 `TenantRvwConfig` - 将租户配置注入到 Skill 的执行上下文中 **任务 2.4:Hybrid Prompt 拼装机制** 修改各 Skill 的 Prompt 获取逻辑: ``` 最终发送给 LLM 的 Prompt = [静态系统协议层] ← promptService.get('RVW_METHODOLOGY_SYSTEM_BASE') + [换行分隔] + [动态业务层] ← tenantRvwConfig.methodologyExpertPrompt 其中 静态系统协议层 强制包含: - 必须以纯 JSON 输出(json_object 模式) - system_metrics 结构(含 20 项 checkpoints) - expert_report_markdown 字段(由LLM按专家要求自由撰写) ``` **任务 2.5:LLM 调用升级 — Structured Outputs 强约束** 修改 `backend/src/common/llm/LLMFactory.ts`(或各 Skill 的 LLM 调用): - 调用 DeepSeek-V3 / GPT-4o 时,传入 `response_format: { type: "json_object" }` - 彻底摒弃仅靠 Prompt 文字要求 JSON 的"软性约束" **任务 2.6:Handlebars 渲染引擎** 新文件:`backend/src/modules/rvw/services/reportRenderer.ts` ```typescript import Handlebars from 'handlebars'; import Zod from 'zod'; // Zod 校验 LLM 结构化输出 const methodologyOutputSchema = z.object({ system_metrics: z.object({ overall_score: z.number().min(0).max(100), conclusion: z.enum(['直接接收', '小修', '大修', '拒稿']), checkpoints: z.array(z.object({ id: z.number(), item: z.string(), status: z.enum(['pass', 'minor_issue', 'major_issue', 'not_mentioned']), finding: z.string(), suggestion: z.string().optional() })).length(20) }), expert_report_markdown: z.string() }); // Handlebars 渲染(使用租户模板或默认模板) export function renderMethodologyReport( data: MethodologyLLMOutput, template?: string ): string { const safeData = methodologyOutputSchema.parse(data); // Zod 兜底 const compiledTemplate = Handlebars.compile(template ?? DEFAULT_METHODOLOGY_TEMPLATE); return compiledTemplate(safeData); } ``` **任务 2.7:默认 Handlebars 模板(硬编码 + 可覆盖)** 为 4 个 Skill 各提供一套默认 Handlebars 模板(内嵌在代码中),租户可在 ADMIN 配置页覆盖: ```handlebars {{! 默认方法学报告模板 }} # 方法学评估报告 **综合评分:** {{system_metrics.overall_score}} 分 | **审稿结论:** {{system_metrics.conclusion}} ## 详细审查意见 {{expert_report_markdown}} ## 20 项检查点覆盖情况 {{#each system_metrics.checkpoints}} - [{{#if (eq status "pass")}}✅{{else if (eq status "minor_issue")}}🟡{{else if (eq status "major_issue")}}🔴{{else}}⬜{{/if}}] **{{id}}. {{item}}**:{{finding}} {{#if suggestion}}> 💡 建议:{{suggestion}}{{/if}} {{/each}} ``` --- ### 5.4 Phase 2 验收标准 - [ ] 内部运营人员在 `TenantDetailPage` 的"智能审稿配置"Tab 中,能为 JTIM 保存独立的方法学 Prompt 和 Handlebars 模板 - [ ] 运行 JTIM 租户的审稿任务,后端日志显示 Prompt 为"静态协议 + JTIM 业务层"拼接版本 - [ ] LLM 返回 `system_metrics` + `expert_report_markdown` 双字段的 JSON,不崩溃 - [ ] Zod 校验可自动补全缺失的 `checkpoints` 字段(填入 `not_mentioned` 默认值) - [ ] Handlebars 渲染出最终报告文本,内容与 JTIM 模板格式一致 --- ## 六、Phase 3:面向外部的期刊业务端重构(预计 2 周) ### 6.1 动态品牌登录页 **任务 3.1:公开租户信息接口** 新增接口(无需鉴权): ``` GET /api/v1/tenants/public-info/:tenantSlug Response: { logoUrl, brandColor, brandName, backgroundImageUrl } ``` **任务 3.2:动态品牌登录页渲染** > **前置条件**:任务 1.6b 已完成,未登录跳转现在携带 `?tenant=jtim&redirect=/jtim/dashboard` 修改现有 `frontend-v2/src/pages/LoginPage.tsx`(而非新建页面,避免重复): ```typescript // 在 LoginPage 初始化时读取 tenant query param const [searchParams] = useSearchParams() const tenantSlug = searchParams.get('tenant') // e.g., 'jtim' const redirectPath = searchParams.get('redirect') // e.g., '/jtim/dashboard' // 若 tenantSlug 非空,拉取公开配置,渲染期刊品牌 useEffect(() => { if (tenantSlug) { fetch(`/api/v1/tenants/public-info/${tenantSlug}`) .then(r => r.json()) .then(config => setBrandConfig(config)) } }, [tenantSlug]) // 登录成功后 onLoginSuccess(() => { navigate(redirectPath ?? '/') }) ``` **渲染逻辑**: - `tenantSlug` 有值 → 显示期刊专属品牌 UI(Logo、品牌色、背景图) - `tenantSlug` 为空 → 显示主站默认 UI(行为不变,向后兼容) --- ### 6.2 稿件管理池(Dashboard 重构) **任务 3.3:稿件列表宽表升级** 修改 `frontend-v2/src/modules/rvw/pages/Dashboard.tsx`: 参考 `AI审稿V1.html` 原型设计: - **顶部统计卡片**:待预审 / AI 审查中 / 需人工复核 / 今日已退修 - **稿件宽表**:稿件编号 / 标题 / 通讯作者 / 综合评分(环形) / 4 维状态图标 / 状态标签 / 操作按钮 - 4 维状态图标:✅ pass / ⚠️ warn / ❌ error / ⏳ pending --- ### 6.3 沉浸式审稿详情页(核心) **任务 3.4:分屏布局重构** 修改 `frontend-v2/src/modules/rvw/components/TaskDetail.tsx`: 参考原型,重构为两栏: - **左侧 3/5**:稿件原文预览(PDF/Word 渲染或纯文本),带浮动工具栏 - **右侧 2/5**:4-Tab 审查工作台(稿约规范 / 方法学 / 数据验证 / 临床评估) **任务 3.5:Human-in-the-Loop 问题确认** 修改各 Report 组件(`EditorialReport`、`MethodologyReport`等): 每个问题卡片底部增加复核操作: ``` [✓ 采纳此问题] [✗ 误报/忽略] ``` - 状态持久化到后端(新增 `review_task_confirmations` 字段或表) - 只有"已采纳"的问题才会进入退修信 **任务 3.6:主报告渲染升级** 修改 `MethodologyReport.tsx` 等: - 优先展示 `expert_report_markdown`(Handlebars 渲染后的纯文本/Markdown) - 不再依赖组件内部"拼接 JSON 字段展示"的旧逻辑 --- ### 6.4 用户-租户绑定 SOP(无需新开发,但必须列入上线 Checklist) > ⚠️ **【审查报告修正 P1】业务闭环缺失的关键补丁**(来自架构审查报告第 4 条): > 系统已有 `platform_schema.tenant_members` 表和用户管理 API,但计划中未说明"谁把责编拉进特定期刊租户"。 > 此项**无需新开发**,但必须写入交付 SOP: > **上线 Checklist(必须完成):** > 1. 内部超管通过 ADMIN 端的【用户管理 → 租户成员管理】,将期刊责编账号添加至对应租户(如 JTIM) > 2. 赋予该用户 RVW 模块权限 > 3. 验证责编登录 `review.xunzhengyixue.com/jtim` 后能看到该期刊的稿件列表(不为空、不报 403) ### 6.5 退修信生成(重点新功能) **任务 3.7:退修信生成后端接口(SSE 流式方案)** > ⚠️ **【审查报告修正 P0】退修信生成必须使用 SSE 流式,不可同步阻塞**(来自架构审查报告第 2 条): > 退修信涉及 LLM 生成长文本,耗时极易超过 SAE 网关 60 秒限制。 > 系统已有 SSE 基础设施(ASL Deep Research V2.0 已验证),直接复用。 > > ⚙️ **【Sprint 审查修正 #2】运维必须调整 ALB 空闲超时时间** > 阿里云 ALB 默认"连接空闲超时时间"为 **15 秒**。LLM 在生成退修信时若有思考停顿,超过 15 秒未推流,ALB 会强制切断 TCP 连接,前端报 SSE 中断。 > **运维执行项**:进入 ALB 控制台 → 监听(443 端口)→ 高级配置 → 将"连接空闲超时时间"调大至 **300 秒**。 接口设计(两步): ``` # 第一步:SSE 流式生成(打字机效果) GET /api/v2/rvw/tasks/:id/revision-letter/stream Body via query: confirmedIssueIds=xxx,yyy Response: text/event-stream,实时推送 letterContent 增量文本 # 第二步:生成完毕后触发 Word 导出(短耗时,同步可接受) POST /api/v2/rvw/tasks/:id/revision-letter/export Body: { letterContent: string } Response: { wordFileUrl: string } ``` 逻辑: 1. 获取已确认的问题列表(按维度分组) 2. 调用 LLM,以 stream 模式生成退修信,通过 SSE 推流到前端 3. 前端生成完毕后,用户确认内容,点击"导出 Word"触发第二步接口 **任务 3.8:前端退修信弹窗(SSE 打字机效果)** > ⚠️ **【审查报告修正 P0-1】严禁使用原生 `EventSource`** > 浏览器原生 `EventSource` 规范层面不支持设置任何自定义 HTTP Header(无法携带 `Authorization: Bearer xxx`,也无法携带 `x-tenant-id`)。直接使用会导致后端 401 / 403。 > **必须使用 `@microsoft/fetch-event-source` 或手写 Fetch ReadableStream:** ```typescript import { fetchEventSource } from '@microsoft/fetch-event-source'; // 安装:npm install @microsoft/fetch-event-source await fetchEventSource(`/api/v2/rvw/tasks/${taskId}/revision-letter/stream`, { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, 'x-tenant-id': tenantSlug, }, onmessage(event) { setLetterContent(prev => prev + event.data); // 打字机效果 }, onerror(err) { /* 错误处理 */ } }); ``` 新建 `RevisionLetterModal.tsx`: - 展示已采纳问题汇总(可再次勾选/取消) - "生成退修信"按钮 - **打字机式实时渲染**:使用 `@microsoft/fetch-event-source`,字符逐步出现 - 生成完毕后显示"导出 Word"按钮(调用第二步同步接口) --- ### 6.5 双轨导出升级 **任务 3.9:内部审稿报告导出** 升级现有 Word 导出功能: - 使用 Handlebars 渲染的 `expert_report_markdown` 替代旧的 JSON 拼接展示 - 保留 4 个维度的分章节结构 **任务 3.10:作者退修信导出** 退修信 Word 文档格式: - 标题:`Revision Required - [稿件标题]` - 信头:期刊名、日期 - 正文:按维度分组的问题与建议(敬语体) - 结尾:编辑署名 --- ### 6.6 Phase 3 验收标准 - [ ] 访问 `review.xunzhengyixue.com/jtim` 显示 JTIM 品牌登录页(Logo/品牌色正确) - [ ] 登录后进入 JTIM 专属工作台,左侧导航只显示 RVW 相关功能 - [ ] 上传稿件后,在 Dashboard 宽表中可看到 4 维状态图标实时更新 - [ ] 审稿完成后,点击"人工复核"进入分屏详情页 - [ ] 在每个问题卡片上点击"采纳"/"忽略",刷新后状态保持 - [ ] 方法学报告优先展示 Handlebars 渲染的 `expert_report_markdown`(格式与 ADMIN 配置模板一致) - [ ] 点击"生成退修信",退修信以打字机效果实时渲染(SSE 流式),生成完毕后可导出 Word - [ ] 责编账号必须已通过 ADMIN 端绑定至 JTIM 租户,否则访问时正确提示 403 --- ## 七、关键技术风险与防范 ### 风险 1:JSON 转义风暴(最高优先级) **风险**:LLM 在 `expert_report_markdown` 字段中输出含换行符、引号的长文本时,JSON.parse 崩溃。 **防范方案:** 1. **API 层强约束**:所有 LLM 调用启用 `response_format: { type: "json_object" }` 2. **引入 jsonrepair**:安装 `jsonrepair` 包,替换直接使用 `JSON.parse` 的地方 3. **Partial 容灾**:继续保持 `Promise.allSettled` + `partial_completed` 机制 ### 风险 2:Handlebars 模板变量错位 **风险**:运营人员配置的模板引用了 LLM 没有输出的字段,渲染报错。 **防范方案:** 1. **Zod 校验先行**:在 Handlebars 渲染前,用 Zod Schema 对 LLM 输出进行校验并填充默认值 2. **防御性模板规范**:模板中大量使用 `{{#if field}}...{{else}}N/A{{/if}}`,并在 ADMIN 端提示 ### 风险 3:方法学超时(已有缓解措施) **风险**:20 检查点 + 长文本生成,超过 8 分钟超时。 **防范方案(V3.0.2 已实施 + V4.0 增强):** 1. ✅ 分治并行(A/B/C 三段)已实施 2. **V4.0 增强**:`expert_report_markdown` 字段让 LLM 汇总输出一份完整报告,减少结构化字段填充压力 3. 快速模式开关(`RVW_METHODOLOGY_FAST_MODE`)规划中 ### 风险 4:多租户数据越权(安全红线) **风险**:A 期刊编辑通过修改参数访问 B 期刊稿件。 **防范方案:** 1. JWT Payload 中 tenantId 不可伪造(服务端签发) 2. auth.middleware.ts 中双重校验(userId 有效性 + tenantId 归属) 3. 所有 RVW Prisma 查询强制附带 `where: { tenantId: request.tenantId }` 4. 开发规范:禁止跳过 tenantId 校验的裸查 --- ## 八、各阶段任务清单(按人员分工) ### 后端工程师 | Phase | 任务编号 | 任务描述 | 工期 | |-------|---------|---------|------| | Phase 1 | 1.1 | 新增 `tenant_rvw_configs` + Prisma 迁移 | 0.5d | | Phase 1 | 1.2 | `review_tasks` 添加 `tenantId` + Prisma 迁移 | 0.5d | | Phase 1 | 1.3 | JWT 注入 tenantId | 1d | | Phase 1 | 1.4 | 中间件双重校验 | 1d | | Phase 1 | 1.5 | Prisma 查询全局守护 | 1d | | Phase 2 | 2.1 | RVW Config CRUD API | 1d | | Phase 2 | 2.3 | SkillExecutor 按租户动态装配 | 1d | | Phase 2 | 2.4 | Hybrid Prompt 拼装机制 | 1.5d | | Phase 2 | 2.5 | LLM Structured Outputs 强约束 | 0.5d | | Phase 2 | 2.6 | Handlebars 渲染引擎 + Zod 校验 | 1.5d | | Phase 2 | 2.7 | 4 个 Skill 默认 Handlebars 模板 | 1d | | Phase 3 | 3.1 | 公开租户信息接口 | 0.5d | | Phase 3 | 3.7 | 退修信生成接口 | 1.5d | | Phase 3 | 3.9 | 内部报告导出升级 | 1d | | Phase 3 | 3.10 | 退修信 Word 导出 | 1d | ### 前端工程师 | Phase | 任务编号 | 任务描述 | 工期 | |-------|---------|---------|------| | Phase 1 | 1.6 | Tenant 识别逻辑 + 全局 Header 注入 | 1d | | Phase 1 | 1.7 | 模块动态过滤 | 0.5d | | Phase 2 | 2.2 | TenantDetailPage 新增"智能审稿配置"Tab(4个Panel) | 3d | | Phase 3 | 3.2 | 动态品牌登录页 | 1.5d | | Phase 3 | 3.3 | Dashboard 稿件宽表升级 | 2d | | Phase 3 | 3.4 | 分屏布局审稿详情页 | 2d | | Phase 3 | 3.5 | Human-in-the-Loop 问题确认 | 1.5d | | Phase 3 | 3.6 | 主报告展示优先 expert_report_markdown | 1d | | Phase 3 | 3.8 | 退修信生成弹窗 | 1.5d | ### 运维工程师 | Phase | 任务编号 | 任务描述 | 工期 | |-------|---------|---------|------| | Phase 1 | 1.8a | 阿里云 DNS 泛解析 `*.review` 记录 | 0.5d | | Phase 1 | 1.8b | 申购 `*.review.xunzhengyixue.com` SSL 证书并挂载 | 1d | | Phase 1 | 1.8c | Nginx 配置子域名转发规则 | 0.5d | | Phase 1 | 1.8d | 后端 CORS 动态白名单配置 | 0.5d | --- ## 九、开发规范对齐要求 ### 9.1 数据库规范(必须遵守) > 对应:`docs/04-开发规范/09-数据库开发规范.md` 三条铁律 - **必须** 使用 `npx prisma migrate dev --name xxx` 生成迁移文件,**禁止** `prisma db push` - 每次 Prisma Schema 变更后,**立即** 在 `docs/05-部署文档/03-待部署变更清单.md` 追加变更记录 - 本次 V4.0 涉及的迁移文件清单(需逐一追加): | 迁移名 | 内容 | Schema | |--------|------|--------| | `add_tenant_rvw_configs` | 新增 `tenant_rvw_configs` 表 | `platform_schema` | | `add_tenant_id_to_review_tasks` | 历史数据平滑迁移(两步走)| `rvw_schema` | ### 9.2 OSS 存储规范(必须遵守) > 对应:`docs/04-开发规范/11-OSS存储开发规范.md` - 稿件文件上传/读取:**必须** 通过 `common/storage/` 统一接口,禁止前端直连 OSS - > ☁️ **【Sprint 审查修正 #4】OSS Bucket 必须配置 CORS 跨域规则** > 前端通过签名 URL 直连 OSS 下载/预览 PDF 时,浏览器会发起 OPTIONS 预检请求。若 Bucket 未配置 CORS,前端直接报 CORS Error 白屏。 > **运维执行项**:阿里云 OSS 控制台 → 对应 Bucket → 权限管理 → 跨域设置,新增规则: > - 允许来源:`https://review.xunzhengyixue.com` > - 允许方法:`GET, PUT, POST, HEAD` > - 允许 Headers:`*` > - 暴露 Headers:`ETag, x-oss-request-id` - 退修信 Word 导出:文件存储 Key 遵循规范路径格式: ``` rvw/{tenantCode}/{taskId}/manuscripts/{uuid}.docx # 原稿 rvw/{tenantCode}/{taskId}/reports/{uuid}.docx # 审稿报告 rvw/{tenantCode}/{taskId}/revision-letters/{uuid}.docx # 退修信 ``` - 报告/退修信下载链接:**必须** 使用带过期时间的签名 URL(Signed URL),禁止公开直链 - 原始文件名(如 "张三投稿.docx")存数据库,OSS Key 使用 UUID ### 9.3 认证授权规范(必须遵守) > 对应:`docs/04-开发规范/10-模块认证规范.md` **后端路由层**(所有新增 API 必须): ```typescript // RVW Config API:运营管理员才能访问 fastify.put('/api/admin/tenants/:id/rvw-config', { preHandler: [authenticate, requirePermission('ops:user-ops')] }, handler); // RVW 业务 API:普通用户 + 模块权限 fastify.get('/api/v2/rvw/tasks', { preHandler: [authenticate, requireModule('RVW')] }, handler); ``` **前端层**: - 普通业务 API:使用 `common/api/axios.ts` 的 `apiClient`(已内置 JWT) - SSE(退修信生成):使用 `@microsoft/fetch-event-source` + 手动从 `getAccessToken()` 获取 Token **Controller 层**: ```typescript // 必须从 request 提取,禁止硬编码 const userId = getUserId(request); const tenantId = request.tenantId; // 由中间件翻译后挂载 ``` ### 9.4 安全规范(必须遵守) > 对应:`docs/04-开发规范/12-安全开发规范.md` **IDOR 防护**(P0 级强制): ```typescript // ✅ 所有 RVW 查询必须带 tenantId const task = await prisma.reviewTasks.findFirst({ where: { id: taskId, tenantId: request.tenantId } }); // ❌ 禁止裸查 const task = await prisma.reviewTasks.findUnique({ where: { id: taskId } }); ``` **公开 API 限流**(防爬): - `GET /api/v1/tenants/public-info/:tenantSlug`(无需鉴权的品牌配置接口)**必须** 加 Rate Limiting(建议 30次/分钟/IP) **Handlebars 模板注入防护**: - 运营人员配置的 Handlebars 模板在服务端渲染时,**必须** 使用 `Handlebars.create()` 独立沙箱实例 - 禁用危险 helpers(`{{raw}}` 等),防止模板注入攻击 - 渲染时捕获所有异常,失败降级返回 `expert_report_markdown` 原始内容 ### 9.5 与 ADMIN 模块融合要点 > 对应:`docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md` | V4.0 需求 | ADMIN 现有能力 | 融合方式 | |-----------|--------------|---------| | Handlebars 模板编辑 + 预览 | Phase 3.5.4 `PromptEditorPage` 已有 Test Render | 在 RVW Config Tab 中复用该组件 | | 方法学业务 Prompt 配置 | Phase 3.5.1 `PromptService` + CodeMirror 编辑器 | 复用 `PromptEditor` 组件 | | 用户-租户绑定 | Phase 4.1 用户管理 + `tenant_members` 表 | 走已有流程,纳入上线 SOP | | 期刊品牌定制(Logo/主题色) | ADMIN P2 待开发:`品牌定制配置` + `租户专属登录页` | **主动对齐**:V4.0 中的动态登录页实现即为该 P2 项落地,需同步更新 ADMIN 开发状态文档 | | 运营数据统计 | Phase 5.0 `ActivityService` 埋点 | 新增退修信生成、配置变更等埋点,使用现有 fire-and-forget 模式 | --- ## 十一、不在本次 V4.0 范围内(明确排除) 以下功能在本次 V4.0 中**不开发**,待后续版本(V4.1+)规划: 1. **投稿作者侧门户**:作者直接上传投稿、查看审稿意见(本次只做责编侧) 2. **期刊主编仪表盘**:全刊数据统计(本次只做责编工作台) 3. **外审专家模块**:分配外审、专家接收稿件(超出本次范围) 4. **配置中心自助访问**:期刊客户自行修改配置(MVP 阶段由内部运营代劳) 5. **微信登录**:认证网关方案规划中,MVP 阶段账密登录 6. **PDF 渲染左栏**:Phase 3 的左侧稿件预览可先用纯文本展示,真正的 PDF 渲染后续迭代 --- ## 十、全局技术依赖与选型 ### 10.1 新增依赖(确需安装) | 包 | 用途 | 安装命令 | |----|------|---------| | `@microsoft/fetch-event-source` | 支持自定义 Header 的 SSE 客户端 | `npm install @microsoft/fetch-event-source`(前端) | | `jsonrepair` | LLM JSON 容错解析 | `npm install jsonrepair`(后端) | | `zod` | LLM 输出 Schema 校验 | `npm install zod`(后端,若未安装) | ### 10.2 复用现有能力(禁止重复造轮子) > ⚠️ 以下能力系统中已存在,**直接调用,严禁重新实现** | 能力 | 现有位置 | RVW V4.0 使用场景 | |------|---------|-----------------| | **SSE 流式响应** | `backend/src/common/streaming/` `createStreamingService` | 退修信生成后端推流(替代自建 SSE) | | **缓存服务** | `backend/src/common/cache/` | 租户 slug → UUID 查询缓存(TTL 5min) | | **Prompt 管理 + Handlebars** | `backend/src/common/prompt/` `PromptService` | Handlebars 报告模板渲染(PromptService 已支持,无需新建 `reportRenderer.ts`) | | **LLM 网关** | `backend/src/common/llm/LLMFactory` | 所有 LLM 调用 | | **认证中间件** | `backend/src/common/auth/auth.middleware.ts` `authenticate` | 所有新增 RVW API 路由 | | **模块中间件** | `backend/src/common/auth/auth.middleware.ts` `requireModule` | RVW Config API 必须加 `requireModule('RVW')` | | **Axios 实例(含认证)** | `frontend-v2/src/common/api/axios.ts` | 所有前端 API 调用(含自动 JWT 注入) | | **Token 获取** | `frontend-v2/src/framework/auth/api.ts` `getAccessToken()` | SSE 手动鉴权(`@microsoft/fetch-event-source` 中使用) | | **运营埋点** | `backend/src/common/logging/ActivityService` | 退修信生成、配置变更等新行为需埋点 | | **存储服务** | `backend/src/common/storage/` | 稿件上传、报告/退修信 Word 导出存储 | | **Prompt 测试渲染** | ADMIN `PromptEditorPage` 已有 Test Render 功能(Phase 3.5.4) | Handlebars 模板预览直接复用 | | **租户详情页** | `frontend-v2/src/pages/admin/tenants/TenantDetailPage.tsx` | 新增"智能审稿配置"Tab,在此文件扩展 | | **用户-租户绑定** | ADMIN 用户管理(Phase 4.1 已完成)| 新期刊用户绑定,走已有流程,无需开发 | | **品牌定制能力** | ADMIN `tenants` 表 `config JSON` 字段 + `P2 - 品牌定制` 待开发项 | 动态登录页的 Logo/主题色配置,与 ADMIN 品牌定制对齐,避免冲突 | ### 10.3 LLM 调用规范 | 项目 | 要求 | |------|------| | Structured Output | 所有新增 LLM 调用启用 `response_format: { type: "json_object" }` | | 通过 `LLMFactory` | 禁止直接 new SDK 客户端 | | maxTokens | 各 Section 分治调用使用 reduced budget(2048),防超时 | --- ## 十二、文档与设计参考 | 文档 | 路径 | |------|------| | PRD | `docs/03-业务模块/RVW-稿件审查系统/00-系统设计/V3.0/AI智能审稿系统(期刊SaaS版)产品需求文档0314.md` | | UI 原型 | `docs/03-业务模块/RVW-稿件审查系统/00-系统设计/V3.0/AI审稿V1.html` | | 技术风险指南 | `docs/03-业务模块/RVW-稿件审查系统/00-系统设计/V3.0/核心技术难点与防范指南.md` | | 域名与多租户指南 | `docs/03-业务模块/RVW-稿件审查系统/00-系统设计/V3.0/AI智能审稿域名与多租户技术指南.md` | | 架构白皮书 | `docs/03-业务模块/RVW-稿件审查系统/08-技术架构建议/智能审稿终极稳定架构白皮书.md` | | 当前模块状态 | `docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md` | | 输出解耦计划 | `docs/03-业务模块/RVW-稿件审查系统/04-开发计划/RVW V4.0 智能审稿输出解耦开发计划.md` | --- --- ## 十三、架构审查修正汇总(v1.0 → v1.3) **第一轮审查修正(v1.0 → v1.1)** | # | 等级 | 修正内容 | 影响章节 | |---|------|---------|---------| | 1 | 🔴 P0 | 历史 `review_tasks` 平滑迁移两步走脚本 | 第四章 4.1 | | 2 | 🔴 P0 | 退修信生成改为 SSE 流式,拆分为两步接口 | 第六章 6.5 | | 3 | 🟡 P1 | Vite `base: '/'` + Nginx `try_files` 明确要求 | 第四章 4.4 | | 4 | 🟡 P1 | 用户-租户绑定列入交付 SOP(无需新开发)| 第六章 6.4 | | 5 | 🟡 P1 | Handlebars 编辑器增加"测试渲染"预览按钮 | 第五章 5.2 | | 6 | 🟢 P2 | 前端携带 `x-tenant-id` Header + 后端从 Header 提取 | 第四章 4.3 | | - | 架构前提 | 域名方案从子域名改为路径隔离 `review.xunzhengyixue.com/:tenantId` | 全文 | **第二轮审查修正(v1.1 → v1.2)** | # | 等级 | 修正内容 | 影响章节 | |---|------|---------|---------| | 7 | 🔴 P0 | SSE 接口严禁使用原生 `EventSource`,必须用 `@microsoft/fetch-event-source` | 第六章 6.5 | | 8 | 🟡 P1 | URL Slug(`jtim`)→ `tenants.code` → 中间件翻译为 `tenants.id`,加 Redis 缓存 | 第四章 4.2 | | 9 | 🟢 P2 | Axios 全局 Header 改为请求拦截器动态读取,防状态污染 | 第四章 4.3 | **第三轮规范对齐补丁(v1.2 → v1.3,已完成实现)** | # | 类型 | 补丁内容 | 来源 | |---|------|---------|------| | A | 🔄 复用 | 退修信 SSE 后端用 `createStreamingService`,不自建 | 通用能力层:`common/streaming/` | | B | 🔄 复用 | 租户 slug 缓存用 `common/cache/` 服务,不自建 Redis 代码 | 通用能力层:`common/cache/` | | C | 🔄 复用 | Handlebars 渲染直接扩展 `PromptService`,不新建 `reportRenderer.ts` | 通用能力层:`common/prompt/` | | D | 🔄 复用 | 前端业务 API 用 `common/api/axios.ts`,SSE 用 `getAccessToken()` | 认证规范 §2 | | E | 🔄 复用 | Handlebars 预览复用 ADMIN `PromptEditorPage` Test Render | ADMIN Phase 3.5.4 | | F | 📏 规范 | 所有新增后端路由加 `authenticate` + `requireModule('RVW')` | 认证规范 §3 | | G | 📏 规范 | 所有新增 Prisma 查询必须带 `tenantId`,防 IDOR | 安全规范 §1 | | H | 📏 规范 | OSS 路径遵循 `rvw/{tenantCode}/{taskId}/xxx/` 格式,报告用签名 URL | OSS 规范 §2-3 | | I | 📏 规范 | DB 每次迁移更新 `docs/05-部署文档/03-待部署变更清单.md` | 数据库规范铁律 | | J | 📏 规范 | 公开接口加 Rate Limiting,Handlebars 渲染加沙箱防注入 | 安全规范 §1/§6 | | K | 🤝 融合 | 动态登录页实现 = ADMIN P2 `品牌定制配置` + `租户专属登录页` 落地,对齐两端 | ADMIN 待开发 P2 | **第四轮:运行时 Bug 修复(v1.3 → v1.4,✅ 已完成实现)** | # | 文件 | 修复内容 | |---|------|---------| | L | `RouteGuard.tsx` | 租户感知重定向:未登录时从 URL 提取 `tenantSlug`,跳转 `/t/jtim/login?redirect=/jtim/dashboard`,保留期刊上下文;主站路径行为不变 | **第五轮:Sprint 冲刺会架构细节修正(v1.4 → v1.5,✅ 已完成实现)** | # | 类型 | 内容 | 影响 | |---|------|------|------| | M | 🏗️ 架构修正 | JWT 不含 `tenantId`,后端实时通过 `x-tenant-id` Header + `tenant_members` 核验 | Task 1.3 | | N | ✅ 已实现 | `useTenantStore` + `useTenantObserver` + `TenantBootstrap` 全链路,解决 SPA 状态陷阱 | Task 1.6b | | O | ✅ 已实现 | `RouteGuard` 动态排除 MODULES 路径,对齐 `/t/:tenantCode/login` 路由 | Task 1.6b | | P | ⚙️ 运维 | ALB 空闲超时从 15s 调至 300s(SSE 退修信生成防切断)| Task 3.7 | | Q | ⚙️ 运维 | OSS Bucket 新增 CORS 规则,允许 `review.xunzhengyixue.com` | Task 9.2 | --- **文档版本:** v1.5 **制定日期:** 2026-03-14 **最后更新:** 2026-03-14(第五轮:Sprint 冲刺会 4 条细节修正 + useTenantObserver 全链路已实现) **下一步行动:** Phase 1 启动 — DB 迁移脚本(含历史数据平滑迁移)+ 运维执行 ALB 超时 + OSS CORS 配置 + `@microsoft/fetch-event-source` 安装 + `/t/:tenantCode/login` 路由渲染期刊品牌 UI