Files
AIclinicalresearch/docs/03-业务模块/IIT Manager Agent/04-开发计划/V3.0全新开发计划/用户权限与多租户三阶段实施计划.md
HaHafeng 0b29fe88b5 feat(iit): QC deep fix + V3.1 architecture plan + project member management
QC System Deep Fix:
- HardRuleEngine: add null tolerance + field availability pre-check (skipped status)
- SkillRunner: baseline data merge for follow-up events + field availability check
- QcReportService: record-level pass rate calculation + accurate LLM XML report
- iitBatchController: legacy log cleanup (eventId=null) + upsert RecordSummary
- seed-iit-qc-rules: null/empty string tolerance + applicableEvents config

V3.1 Architecture Design (docs only, no code changes):
- QC engine V3.1 plan: 5-level data structure (CDISC ODM) + D1-D7 dimensions
- Three-batch implementation strategy (A: foundation, B: bubbling, C: new engines)
- Architecture team review: 4 whitepapers reviewed + feedback doc + 4 critical suggestions
- CRA Agent strategy roadmap + CRA 4-tool explanation doc for clinical experts

Project Member Management:
- Cross-tenant member search and assignment (remove tenant restriction)
- IIT project detail page enhancement with tabbed layout (KB + members)
- IitProjectContext for business-side project selection
- System-KB route access control adjustment for project operators

Frontend:
- AdminLayout sidebar menu restructure
- IitLayout with project context provider
- IitMemberManagePage new component
- Business-side pages adapt to project context

Prisma:
- 2 new migrations (user-project RBAC + is_demo flag)
- Schema updates for project member management

Made-with: Cursor
2026-03-01 15:27:05 +08:00

443 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CRA Agent V3.0 — 用户权限与多租户三阶段实施计划
> **版本:** v1.0
> **日期:** 2026-02-28
> **状态:** 待实施
> **前置依赖:** P0 + P1 已完成(质控流水线 + ChatOrchestrator
> **关联文档:**
> - [V3.0 全新开发计划](./V3.0全新开发计划.md)
> - [统一数字 CRA 质控平台 PRD](./统一数字%20CRA%20质控平台产品需求文档(PRD).md)
---
## 1. 问题背景
### 1.1 当前系统存在的三个核心问题
**问题 A业务端 CRA 质控平台无法运行**
- 所有业务端页面驾驶舱、eQuery、报告、AI 对话)从 URL 参数 `?projectId=xxx` 获取项目 ID
- 没有任何机制把登录用户与 IIT 项目关联,导致页面显示"请先选择一个项目"
- 侧边栏项目信息为硬编码假数据("IIT-2026-001"
**问题 B运营管理端 IIT 项目管理定位不当**
- "IIT 项目管理"与"Prompt 管理""租户管理""用户管理"混在同一菜单中
- 普通项目运营人员不应看到平台级管理功能
- 药企/医院客户的项目管理人员不应看到其他企业的项目
**问题 C用户-项目关联机制缺失**
- `IitUserMapping.systemUserId` 存储的是随意字符串(当前值为 `"FengZhiBo"`),未关联平台 `User`
- `IitProject` 没有 `tenantId` 字段,无法实现租户隔离
- IIT API 路由没有认证中间件,无访问控制
### 1.2 当前数据库实际状态
```
-- iit_schema.projects1 条记录)
id: test0102-pd-study | name: test0207 | status: active
-- iit_schema.user_mappings1 条记录)
system_user_id: FengZhiBo | wecom_user_id: FengZhiBo | role: PI
-- public.users1 条记录)
id: user-mock-001 | name: 测试用户 | role: user
(注:此用户为占位假数据,非真实用户)
```
两张表之间没有任何外键关联。
### 1.3 当前角色体系
**平台级角色public.users.role**
| 角色 | 说明 | 当前使用 |
|------|------|---------|
| SUPER_ADMIN | 平台超级管理员 | 可访问运营管理端全部功能 |
| PROMPT_ENGINEER | Prompt 工程师 | 可访问运营管理端 |
| HOSPITAL_ADMIN | 医院管理员 | 已定义,未使用 |
| PHARMA_ADMIN | 药企管理员 | 已定义,未使用 |
| DEPARTMENT_ADMIN | 科室管理员 | 已定义,未使用 |
| USER | 普通用户 | 默认角色 |
**IIT 项目级角色iit_schema.user_mappings.role**
| 角色 | 说明 | 当前使用 |
|------|------|---------|
| PI | 主要研究者 | 已使用 |
| Sub-I | 次要研究者 | 已定义 |
| CRC | 临床研究协调员 | 已定义 |
| CRA | 临床监查员 | 已定义 |
| DM | 数据管理员 | 已定义 |
| Statistician | 统计师 | 已定义 |
| Other | 其他 | 已定义 |
**缺失的角色:**
| 角色 | 层级 | 说明 |
|------|------|------|
| **PM** | IIT 项目级 | 项目管理员,负责项目配置和管理 |
| **IIT_OPERATOR** | 平台级 | IIT 项目运营负责为客户创建和配置项目Phase 2 实现) |
---
## 2. 三阶段实施计划
```
Phase 1立即 Phase 2近期 Phase 3中期
让业务端能跑 用户-项目关联 多租户隔离
───────────────── → ──────────────────── → ─────────────────
自动选中活跃项目 systemUserId 关联 User IitProject 加 tenantId
Provider 注入上下文 /my-projects API 项目按租户过滤
管理端侧边栏分组 项目角色权限矩阵 PHARMA_ADMIN 自助管理
配置功能补全 IIT 路由加认证
IIT_OPERATOR 平台角色
```
---
## 3. Phase 1让业务端能跑预估 0.5 天)
### 3.1 目标
- CRA 质控平台业务端页面能正常加载和显示数据
- 运营管理端 IIT 项目管理菜单位置合理
- 项目配置功能补全(定时质控、变量清单)
### 3.2 方案
#### 3.2.1 创建 IitProjectContext Provider
**设计原则:** 通用方案,适用于用户关联 0 / 1 / N 个项目的所有场景。
**核心逻辑:**
```
用户进入 /iit → IitProjectProvider 初始化
→ 调用 GET /api/v1/admin/iit-projects 获取项目列表
→ 过滤 status = 'active'
→ 0 个 → 显示空状态页:"暂无关联的 IIT 项目"
→ 1 个 → 自动选中,不弹选择器
→ N 个 → 恢复 localStorage 上次选择;若无记录,默认选第一个
→ 用户可随时通过侧边栏顶部下拉选择器切换项目
→ 切换后写入 localStorage子页面自动刷新
→ 通过 useIitProject() hook 暴露:
{ projectId, project, projects, loading, switchProject(id) }
```
**项目选择器 UI**
放在 IitLayout 侧边栏顶部(替换当前硬编码的项目名称),具体表现:
- 1 个项目:显示项目名称(纯文本,无下拉箭头)
- N 个项目:显示当前项目名 + 下拉箭头,点击弹出 Select 选择器
- 选择器选项:项目名称 + 项目编号(如 "原发性痛经队列研究 / IIT-2026-001"
- 切换项目后,所有子页面通过 Context 自动获取新 projectId 并刷新数据
**改动文件:**
| 文件 | 改动 |
|------|------|
| `frontend-v2/src/modules/iit/context/IitProjectContext.tsx` | 新建 Context + Provider + useIitProject hook |
| `frontend-v2/src/modules/iit/IitLayout.tsx` | 集成 Provider侧边栏顶部改为项目选择器 |
| `frontend-v2/src/modules/iit/pages/DashboardPage.tsx` | 删除 URL 参数读取,改用 `useIitProject()` |
| `frontend-v2/src/modules/iit/pages/EQueryPage.tsx` | 同上 |
| `frontend-v2/src/modules/iit/pages/ReportsPage.tsx` | 同上 |
| `frontend-v2/src/modules/iit/pages/AiStreamPage.tsx` | 同上 |
| `frontend-v2/src/modules/iit/pages/AiChatPage.tsx` | 同上 |
#### 3.2.2 管理端侧边栏三区重组(方案 B逻辑拆分
**决策背景:** 概念上存在三种管理职能(平台管理、项目运营、商务运营),但当前阶段团队规模小,同一人可能跨职能操作。采用**方案 B逻辑拆分**:保留单一 `/admin` 入口,侧边栏按职能分为三个带标题的菜单组,通过 RBAC 控制每个角色看到哪些组。未来用户群体分化后可平滑升级为物理拆分(独立路由 + 独立 Layout
**侧边栏结构SUPER_ADMIN 视角,看到全部):**
```
┌─ 平台管理 ─────────────┐
│ 运营概览 │
│ Prompt 管理 │
│ 系统知识库 │
│ LLM 配置 │
│ 系统设置 │
├─ 项目运营 ─────────────┤
│ IIT 项目管理 │
├─ 商务运营 ─────────────┤
│ 租户管理 │
│ 用户管理 │
└────────────────────────┘
```
**各角色可见性:**
| 菜单组 | SUPER_ADMIN | PROMPT_ENGINEER | IIT_OPERATOR | PHARMA_ADMIN |
|--------|:-----------:|:---------------:|:------------:|:------------:|
| 平台管理 | 全部 | Prompt 管理 + 系统知识库 | - | - |
| 项目运营 | 全部项目 | - | 全部项目 | 本租户项目 |
| 商务运营 | 全部 | - | - | - |
**改动文件:** `frontend-v2/src/framework/layout/AdminLayout.tsx`
#### 3.2.3 项目配置功能补全
**A. 定时质控配置 UI**
数据库 `iit_schema.projects` 已有 `cron_enabled``cron_expression` 字段,前端未暴露。
`IitProjectDetailPage.tsx` 的"REDCap 配置"Tab 中增加:
- 定时质控开关Switch绑定 `cronEnabled`
- Cron 表达式输入Input + 预设选项:"每天 8:00" / "每周一 9:00" / 自定义)
**B. 变量清单 Tab**
后端 `GET /:id/field-metadata` API 已存在。在 `IitProjectDetailPage.tsx` 新增第 5 个 Tab"变量清单",展示 REDCap 变量表格。
**C. PM 角色新增**
`iitUserMappingService.ts``getRoleOptions()` 中添加:
```
{ value: 'PM', label: '项目管理员 (PM)' }
```
### 3.3 Phase 1 不做的事
- 不改数据库 Schema
- 不做用户-项目关联systemUserId 改造)
- 不加认证中间件
- 不做租户隔离
---
## 4. Phase 2用户-项目关联 + 角色权限(预估 2 天)
### 4.1 目标
- 登录用户自动看到自己关联的 IIT 项目
- IIT 项目角色对应明确的权限
- IIT API 路由有认证保护
- 运营管理端按角色控制菜单可见性
- 新增 IIT_OPERATOR 平台角色,使项目运营人员无需 SUPER_ADMIN 权限即可创建和配置 IIT 项目
### 4.2 数据模型改动
#### 4.2.1 IitUserMapping 增强
```prisma
model IitUserMapping {
// ... 现有字段 ...
systemUserId String @map("system_user_id") // 改为必须是 User.id
// 新增:
userId String? @map("user_id") // 平台 User 表外键(可选,渐进式关联)
// 新增关联
user User? @relation(fields: [userId], references: [id])
}
```
**迁移策略:**
- 新增 `userId` 字段nullable不破坏现有数据
- 新建用户映射时要求填写真实 `userId`
- 旧数据逐步补齐
#### 4.2.2 IitProject 增强(为 Phase 3 预留)
```prisma
model IitProject {
// ... 现有字段 ...
// 新增:
tenantId String? @map("tenant_id") // 租户归属Phase 3 正式启用)
}
```
### 4.3 API 改动
#### 4.3.1 新增接口
```
GET /api/v1/iit/my-projects
```
逻辑:
1. 从 JWT Token 获取当前用户 `userId`
2. 查询 `IitUserMapping WHERE userId = :userId`
3. 返回关联的项目列表 + 用户在每个项目中的角色
响应示例:
```json
{
"projects": [
{
"id": "test0102-pd-study",
"name": "test0207",
"status": "active",
"myRole": "PI",
"description": "原发性痛经队列研究"
}
]
}
```
#### 4.3.2 IIT 路由加认证
所有 `/api/v1/admin/iit-projects` 路由添加 `authenticate` 中间件。
#### 4.3.3 项目级权限检查
```
中间件requireProjectRole(projectId, allowedRoles)
→ 查 IitUserMapping WHERE projectId AND userId
→ 检查 role 是否在 allowedRoles 中
```
### 4.4 项目角色权限矩阵
| 功能 | PM | PI | CRA | CRC | DM | Sub-I | Statistician |
|------|----|----|-----|-----|----|-------|-------------|
| 查看驾驶舱 | 读 | 读 | 读 | 读 | 读 | 读 | 读 |
| 查看/处理 eQuery | 读写 | 读 | 读写 | 读写 | 读 | 读 | - |
| 查看报告 | 读 | 读 | 读 | 读 | 读 | 读 | 读 |
| AI 对话 | 是 | 是 | 是 | 是 | 是 | 是 | 是 |
| 项目配置REDCap/规则/通知/知识库) | 读写 | - | - | - | - | - | - |
| 查看变量清单 | 读 | 读 | 读 | 读 | 读 | 读 | 读 |
| 管理质控规则 | 读写 | - | 读 | - | 读 | - | - |
### 4.5 管理端菜单权限(承接 Phase 1 三区分组)
Phase 1 已完成侧边栏三区分组的 UI 结构Phase 2 加入真正的 RBAC 控制:
| 角色 | 可进入 /admin | 平台管理 | 项目运营 | 商务运营 |
|------|:------------:|:-------:|:-------:|:-------:|
| SUPER_ADMIN | 是 | 全部 | 全部项目 | 全部 |
| PROMPT_ENGINEER | 是 | Prompt + 知识库 | - | - |
| IIT_OPERATOR本阶段新增 | 是 | - | 全部项目 | - |
| PHARMA_ADMIN | 是 | - | 本租户项目 | - |
| HOSPITAL_ADMIN | 是 | - | 本租户项目 | - |
| USER | 否 | - | - | - |
**实现方式:**
- 路由守卫:`/admin` 入口检查 `user.role` 是否在允许列表中
- 侧边栏渲染:根据 `user.role` 过滤 `menuItems`,角色看不到的组直接不渲染
- API 层:每个管理端 API 添加 `authenticate` + `requireRole(allowedRoles)` 中间件
### 4.6 前端改动
- `IitProjectContext` 改为调用 `/api/v1/iit/my-projects`
- 根据 `myRole` 控制业务端 Tab 可见性PM 看到更多设置入口)
- `AdminLayout.tsx``user.role` 过滤侧边栏菜单组可见性Phase 1 已分组,此处加权限判断)
- `/admin` 路由入口添加角色守卫
---
## 5. Phase 3多租户隔离预估 1.5 天)
### 5.1 目标
- IIT 项目按租户隔离,药企只能看到自己的项目
- PHARMA_ADMIN 可在运营管理端管理自己租户的 IIT 项目
- SUPER_ADMIN 可跨租户查看所有项目
### 5.2 数据模型
启用 Phase 2 预留的 `tenantId` 字段,设为必填:
```prisma
model IitProject {
tenantId String @map("tenant_id")
tenant Tenant @relation(fields: [tenantId], references: [id])
}
```
### 5.3 API 改动
所有项目查询接口添加租户过滤:
```sql
-- 非 SUPER_ADMIN
SELECT * FROM iit_schema.projects WHERE tenant_id = :currentUserTenantId
-- SUPER_ADMIN
SELECT * FROM iit_schema.projects -- 无限制
```
### 5.4 项目创建流程
```
Phase 2 完成后:
SUPER_ADMIN 或 IIT_OPERATOR 创建项目 → 手动指定 tenantId选择为哪个客户创建
Phase 3 完成后(客户自助):
PHARMA_ADMIN 创建项目 → 自动绑定自己的 tenantId
```
### 5.5 前端改动
- 运营管理端项目列表SUPER_ADMIN 看到全部 + 租户筛选器PHARMA_ADMIN 只看到自己的
- 项目创建表单SUPER_ADMIN 需选择租户PHARMA_ADMIN 自动绑定
---
## 6. 实施优先级总结
| 阶段 | 核心内容 | 预估工时 | 数据库改动 | 前置条件 |
|------|---------|---------|-----------|---------|
| **Phase 1** | Provider + 管理端分组 + 配置补全 | 0.5 天 | 无 | 无 |
| **Phase 2** | userId 关联 + /my-projects + 角色权限 + 认证 + IIT_OPERATOR 角色 | 2 天 | 新增 userId 列、tenantId 列、IIT_OPERATOR 角色枚举 | Phase 1 |
| **Phase 3** | 租户隔离 + PHARMA_ADMIN 自助 | 1.5 天 | tenantId 改为必填 | Phase 2 |
### Phase 1 立即可执行的原因
- 0 数据库改动
- 只改前端代码
- 当前只有 1 个活跃项目 + 1 个测试用户,自动选中即可
- 让业务端立即可演示
### Phase 2 的触发条件
- 有真实用户需要登录系统
- 需要区分不同用户看到不同项目
- 需要保护 API 安全
### Phase 3 的触发条件
- 有多个客户(药企/医院)同时使用系统
- 需要租户级别的数据隔离
---
## 7. 附录:改动文件清单
### Phase 1
| 文件 | 类型 | 说明 |
|------|------|------|
| `frontend-v2/src/modules/iit/context/IitProjectContext.tsx` | 新建 | 项目上下文 Provider |
| `frontend-v2/src/modules/iit/IitLayout.tsx` | 修改 | 集成 Provider + 真实项目信息 |
| `frontend-v2/src/modules/iit/pages/DashboardPage.tsx` | 修改 | 改用 useIitProject() |
| `frontend-v2/src/modules/iit/pages/EQueryPage.tsx` | 修改 | 改用 useIitProject() |
| `frontend-v2/src/modules/iit/pages/ReportsPage.tsx` | 修改 | 改用 useIitProject() |
| `frontend-v2/src/modules/iit/pages/AiStreamPage.tsx` | 修改 | 改用 useIitProject() |
| `frontend-v2/src/modules/iit/pages/AiChatPage.tsx` | 修改 | 改用 useIitProject() |
| `frontend-v2/src/framework/layout/AdminLayout.tsx` | 修改 | 侧边栏分组 |
| `frontend-v2/src/modules/admin/pages/IitProjectDetailPage.tsx` | 修改 | 定时质控配置 + 变量清单 Tab |
| `backend/src/modules/admin/iit-projects/iitUserMappingService.ts` | 修改 | 添加 PM 角色 |
### Phase 2
| 文件 | 类型 | 说明 |
|------|------|------|
| `backend/prisma/schema.prisma` | 修改 | IitUserMapping 加 userId、IitProject 加 tenantId |
| `backend/src/modules/iit-manager/routes/index.ts` | 修改 | 加 /my-projects 路由 + authenticate 中间件 |
| `backend/src/common/auth/auth.middleware.ts` | 修改 | 新增 requireProjectRole 中间件 |
| `backend/prisma/schema.prisma` (Role enum) | 修改 | 新增 IIT_OPERATOR 角色枚举值 |
| `frontend-v2/src/modules/iit/context/IitProjectContext.tsx` | 修改 | 改调 /my-projects |
| `frontend-v2/src/framework/layout/AdminLayout.tsx` | 修改 | 按角色控制菜单组可见性IIT_OPERATOR 只看项目运营) |
### Phase 3
| 文件 | 类型 | 说明 |
|------|------|------|
| `backend/prisma/schema.prisma` | 修改 | tenantId 改为必填 |
| `backend/src/modules/admin/iit-projects/iitProjectService.ts` | 修改 | 查询加租户过滤 |
| `frontend-v2/src/modules/admin/pages/IitProjectListPage.tsx` | 修改 | 租户筛选器 |
| `frontend-v2/src/modules/admin/pages/IitProjectDetailPage.tsx` | 修改 | 创建时选择租户 |