Files
AIclinicalresearch/docs/03-业务模块/ADMIN-运营管理端/00-系统设计/00-权限与角色体系梳理报告_v1.0.md
HaHafeng 66255368b7 feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1):
- Database: Add user_modules table for fine-grained module permissions
- Database: Add 4 user permissions (view/create/edit/delete) to role_permissions
- Backend: UserService (780 lines) - CRUD with tenant isolation
- Backend: UserController + UserRoutes (648 lines) - 13 API endpoints
- Backend: Batch import users from Excel
- Frontend: UserListPage (412 lines) - list/filter/search/pagination
- Frontend: UserFormPage (341 lines) - create/edit with module config
- Frontend: UserDetailPage (393 lines) - details/tenant/module management
- Frontend: 3 modal components (592 lines) - import/assign/configure
- API: GET/POST/PUT/DELETE /api/admin/users/* endpoints

Architecture Upgrade - Module Permission System:
- Backend: Add getUserModules() method in auth.service
- Backend: Login API returns modules array in user object
- Frontend: AuthContext adds hasModule() method
- Frontend: Navigation filters modules based on user.modules
- Frontend: RouteGuard checks requiredModule instead of requiredVersion
- Frontend: Remove deprecated version-based permission system
- UX: Only show accessible modules in navigation (clean UI)
- UX: Smart redirect after login (avoid 403 for regular users)

Fixes:
- Fix UTF-8 encoding corruption in ~100 docs files
- Fix pageSize type conversion in userService (String to Number)
- Fix authUser undefined error in TopNavigation
- Fix login redirect logic with role-based access check
- Update Git commit guidelines v1.2 with UTF-8 safety rules

Database Changes:
- CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled)
- ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code)
- INSERT 4 permissions + role assignments
- UPDATE PUBLIC tenant with 8 module subscriptions

Technical:
- Backend: 5 new files (~2400 lines)
- Frontend: 10 new files (~2500 lines)
- Docs: 1 development record + 2 status updates + 1 guideline update
- Total: ~4900 lines of code

Status: User management 100% complete, module permission system operational
2026-01-16 13:42:10 +08:00

1383 lines
40 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# **AIclinicalresearch 权限与角色体系梳理报告**
> **文档版本:** v1.1整合Prompt管理需求
> **创建日期:** 2026-01-11
> **最后更新:** 2026-01-11
> **作者:** AI架构师
> **目的:** 系统性梳理当前权限实现状况,为运营管理端和机构管理端开发做准备
> **变更说明:** 整合反馈建议 + Prompt管理系统需求
---
## 📋 目录
1. [当前系统状态分析](#1-当前系统状态分析)
2. [数据库层面梳理](#2-数据库层面梳理)
3. [后端权限实现梳理](#3-后端权限实现梳理)
4. [前端权限实现梳理](#4-前端权限实现梳理)
5. [差距分析](#5-差距分析)
6. [新PRD需求解读](#6-新prd需求解读)
7. [架构设计建议](#7-架构设计建议)
8. [实施路线图](#8-实施路线图)
9. [🆕 Prompt管理系统整合](#9-prompt管理系统整合)
10. [🆕 反馈采纳说明](#10-反馈采纳说明)
---
## 1. 当前系统状态分析
### 1.1 核心发现 🔍
**✅ 已有基础**
- 数据库有基础的User表`platform_schema.users``public.users`两个版本)
- 有基本的role字段默认值"user"
- 前端有权限框架(`PermissionContext`),但**仅mock数据**
- 后端**完全没有认证/授权系统**
**❌ 缺失关键能力**
1. **没有登录/注册API**
2. **没有JWT认证中间件**
3. **没有租户(Tenant)体系**
4. **没有角色权限系统(RBAC)**
5. **没有Feature Flag控制**
6. **没有审计日志系统**
**🎯 当前状态**
- 系统处于**单测试账号**阶段
- 所有API都是**无认证、无鉴权**状态
- 前端权限控制是**纯展示性**的mock实现
---
## 2. 数据库层面梳理
### 2.1 当前User表结构
#### **platform_schema.users** ✅ 新架构Prisma定义
```prisma
model User {
id String @id @default(uuid())
email String @unique
password String
name String?
avatarUrl String? @map("avatar_url")
role String @default("user") // ⚠️ 简单字符串,不够用
status String @default("active")
kbQuota Int @default(3)
kbUsed Int @default(0)
trialEndsAt DateTime? @map("trial_ends_at")
isTrial Boolean @default(true)
lastLoginAt DateTime? @map("last_login_at")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@schema("platform_schema")
}
```
#### **public.users** ❌ 旧表(历史遗留)
```prisma
model users {
id String
email String @unique
password String
name String?
avatar_url String?
role String @default("user") // ⚠️ 同样简单
status String @default("active")
kb_quota Int @default(3)
kb_used Int @default(0)
// ... 其他字段
@@schema("public")
}
```
**⚠️ 问题:**
- 两个User表并存命名不一致users vs User
- `role`字段仅为字符串无enum约束
- **没有租户关联字段tenantId**
- **没有部门字段department**
- **没有权限配置字段**
### 2.2 缺失的核心表
根据PRD需求需要新增以下表
| 表名 | Schema位置 | 用途 | 优先级 |
|------|-----------|------|--------|
| **tenants** | platform_schema | 租户主表(医院、药企、期刊) | P0 |
| **tenant_users** | platform_schema | 租户-用户关联表 | P0 |
| **departments** | platform_schema | 部门/科室表 | P1 |
| **feature_flags** | platform_schema | Feature Flag配置 | P0 |
| **tenant_modules** | platform_schema | 租户订阅模块配置 | P0 |
| **tenant_quotas** | platform_schema | 租户配额管理 | P1 |
| **admin_operation_logs** | admin_schema | 运营操作日志 | P1 |
**注意:** 当前有一个`AdminLog`表在`public`schema但不完整。
### 2.3 已有的审计日志
**IIT模块的审计日志** ✅ (可参考):
```prisma
model IitAuditLog {
id String @id @default(uuid())
projectId String
userId String
actionType String
entityType String
entityId String
details Json?
traceId String
createdAt DateTime @default(now())
@@schema("iit_schema")
}
```
**可复用性:** ✅ 架构设计优秀,可作为全局审计日志的参考。
---
## 3. 后端权限实现梳理
### 3.1 认证系统状态 ❌ **未实现**
**搜索结果:**
- ❌ 没有找到 `/api/auth/login``/api/auth/register`
- ❌ 没有JWT生成/验证工具
- ❌ 没有认证中间件(如 `requireAuth`
**对比其他项目**从codebase_search结果
- ODJ项目有完整的Passport JWT认证 ✅
- BYSY项目有JWT认证中间件 ✅
- **本项目:完全空白** ❌
### 3.2 授权系统状态 ❌ **未实现**
**缺失内容:**
- ❌ 没有角色检查中间件(如 `requireRole(['admin'])`
- ❌ 没有权限映射表ROLE_PERMISSIONS
- ❌ 没有Feature Flag检查逻辑
**影响:**
- 所有API端点都是公开的无权限保护
- 无法区分管理员和普通用户
- 无法实现多租户数据隔离
### 3.3 当前API结构
**Legacy Routes** (无认证)
```
/api/v1/aia/* - AI智能问答无权限检查
/api/v1/pkb/* - 个人知识库(无权限检查)
/api/v1/rvw/* - 稿件审查(无权限检查)
```
**问题:**
- 任何人都可以访问任何用户的数据
- 没有 `userId` 鉴权逻辑
- 没有租户数据隔离
---
## 4. 前端权限实现梳理
### 4.1 权限框架存在 ✅ 但仅为Mock
**文件位置:** `frontend-v2/src/framework/permission/PermissionContext.tsx`
**核心代码:**
```typescript
// ⚠️ 硬编码为最高权限,仅供开发测试
const MOCK_USER: UserInfo = {
id: 'test-user-001',
name: '测试研究员',
email: 'test@example.com',
version: 'premium', // 👈 硬编码
avatar: null,
isTrial: false,
}
// 权限检查函数基于UserVersion等级
const checkModulePermission = (requiredVersion?: UserVersion): boolean => {
if (!user) return false
if (!requiredVersion) return true
return checkVersionLevel(user.version, requiredVersion)
}
```
**UserVersion定义**
```typescript
// framework/permission/types.ts
export type UserVersion = 'free' | 'basic' | 'professional' | 'premium'
```
### 4.2 权限检查逻辑 ✅ 架构完整
**模块注册时的权限声明:**
```typescript
// 模块定义接口
interface ModuleDefinition {
id: string
name: string
path: string
requiredVersion?: UserVersion // 🎯 权限要求
// ...
}
```
**路由守卫:**
```typescript
// RouteGuard.tsx
if (module.requiredVersion && !checkModulePermission(module.requiredVersion)) {
return <PermissionDenied />
}
```
**✅ 优点:**
- 权限框架设计完善
- 易于扩展到真实认证
**❌ 缺点:**
- 用户信息完全hardcode
- 没有对接后端API
- 没有登录/登出UI
---
## 5. 差距分析
### 5.1 与PRD需求的差距
| PRD需求 | 当前状态 | 差距 | 优先级 |
|---------|---------|------|--------|
| **租户管理** | ❌ 无 | 需完整实现Tenant体系 | P0 |
| **4种角色** (SUPER_ADMIN/HOSPITAL_ADMIN/PHARMA_ADMIN/USER) | ❌ 只有简单role字符串 | 需RBAC体系 | P0 |
| **品牌定制** (Logo/登录页) | ❌ 无 | 需tenant.config JSONB字段 | P0 |
| **登录系统** | ❌ 无 | 需JWT认证系统 | P0 |
| **权限控制** | ✅ 前端Mock / ❌ 后端无 | 需后端中间件 | P0 |
| **Feature Flag** | ❌ 无 | 需配置表+检查逻辑 | P0 |
| **运营端** (/admin/*) | ❌ 无 | 需全新开发 | P0 |
| **机构端** (/org/hospital/*, /org/pharma/*) | ❌ 无 | 需全新开发 | P1 |
| **审计日志** | ⚠️ 仅IIT模块 | 需全局审计系统 | P1 |
### 5.2 架构层面差距
**当前架构:** 单用户、无租户、无权限
```
User (单表)
Projects/KnowledgeBases/... (直接关联 userId)
```
**目标架构:** 多租户、RBAC、数据隔离
```
Tenant (租户)
Department (部门/科室)
User (用户) + Role (角色)
Projects/KnowledgeBases/... (tenant_id + user_id)
```
---
## 6. 新PRD需求解读
### 6.1 核心角色定义来自PRD v2.1
| 角色Code | 归属 | 权限范围 | URL前缀 | 核心职责 |
|---------|------|---------|---------|---------|
| **SUPER_ADMIN** | 平台 | 全局数据 | /admin | 租户开通、品牌配置、Prompt调优 |
| **HOSPITAL_ADMIN** | 医院租户 | 本院数据 | /org/hospital | 科室管理、配额分配 |
| **PHARMA_ADMIN** | 药企租户 | 本企项目 | /org/pharma | 项目监控、CRO管理、审计 |
| **USER** | 任意租户 | 个人/被授权数据 | /app | 科研业务操作 |
### 6.2 租户类型Tenant Type
```typescript
enum TenantType {
HOSPITAL = 'HOSPITAL', // 医院客户
PHARMA = 'PHARMA', // 药企客户
JOURNAL = 'JOURNAL', // 期刊客户
}
```
### 6.3 品牌定制需求 🆕
**URL策略**
```
通用登录https://app.yizhengxun.com/auth/login
专属登录https://app.yizhengxun.com/t/{tenant_code}/login
```
**租户配置JSONB**
```json
{
"branding": {
"logoUrl": "https://oss.../jst_logo.png",
"loginBackgroundUrl": "https://oss.../jst_bldg.jpg",
"primaryColor": "#0056b3",
"welcomeTitle": "北京积水潭医院 AI 临床科研平台",
"welcomeSubTitle": "智能化 · 规范化 · 高效率"
}
}
```
### 6.4 智能路由分发(登录后跳转)
```typescript
function getRedirectPath(user, tenant) {
if (user.role === 'SUPER_ADMIN') return '/admin/dashboard';
if (user.role === 'TENANT_ADMIN') {
if (tenant.type === 'HOSPITAL') return '/org/hospital/dashboard';
if (tenant.type === 'PHARMA') return '/org/pharma/dashboard';
}
if (tenant.type === 'JOURNAL') return '/app/rvw/dashboard';
return '/app/dashboard'; // 默认:普通用户
}
```
---
## 7. 架构设计建议
### 7.1 数据库Schema设计
#### **7.1.1 platform_schema平台核心表**
**A. tenants 表** P0
```prisma
model Tenant {
id String @id @default(uuid())
name String // 租户名称(如:北京积水潭医院)
code String @unique // 租户代码jst-hospital用于URL
type TenantType // 租户类型HOSPITAL/PHARMA/JOURNAL
status String @default("active") // active/suspended/expired
// 品牌配置JSONB
config Json @default("{}") // branding配置、模块订阅等
// 配额管理
tokenQuota Int? // 总Token额度
tokenUsed Int @default(0) // 已使用Token
// 时间戳
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime? // 订阅到期时间
// 关系
users TenantUser[]
departments Department[]
modules TenantModule[]
@@index([code])
@@index([type])
@@index([status])
@@map("tenants")
@@schema("platform_schema")
}
enum TenantType {
HOSPITAL
PHARMA
JOURNAL
@@schema("platform_schema")
}
```
**B. users 表扩展** P0
```prisma
model User {
id String @id @default(uuid())
email String @unique
password String
name String?
avatarUrl String? @map("avatar_url")
// 🆕 多租户支持
tenantId String? @map("tenant_id") // 所属租户NULL=平台管理员)
departmentId String? @map("department_id") // 所属部门/科室
// 🆕 角色系统
role UserRole // SUPER_ADMIN/TENANT_ADMIN/USER
// 其他字段保持不变...
status String @default("active")
kbQuota Int @default(3)
trialEndsAt DateTime?
lastLoginAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关系
tenant Tenant? @relation(fields: [tenantId], references: [id])
department Department? @relation(fields: [departmentId], references: [id])
@@index([tenantId])
@@index([departmentId])
@@index([role])
@@map("users")
@@schema("platform_schema")
}
enum UserRole {
SUPER_ADMIN // 平台超级管理员
TENANT_ADMIN // 租户管理员(医院/药企)
USER // 普通用户(医生/研究员)
@@schema("platform_schema")
}
```
**C. tenant_members 表** P0 采纳反馈改名为TenantMember
```prisma
model TenantMember {
id String @id @default(uuid())
tenantId String @map("tenant_id")
userId String @map("user_id")
role String // 在该租户中的角色
joinedAt DateTime @default(now()) @map("joined_at")
tenant Tenant @relation(fields: [tenantId], references: [id])
@@unique([tenantId, userId])
@@map("tenant_members") // 语义更清晰
@@schema("platform_schema")
}
```
**D. departments 表** P1
```prisma
model Department {
id String @id @default(uuid())
tenantId String @map("tenant_id")
name String // 科室名称(如:心内科)
parentId String? @map("parent_id") // 上级科室(支持树形结构)
tokenQuota Int? @map("token_quota") // 科室Token额度
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
users User[]
parent Department? @relation("DepartmentHierarchy", fields: [parentId], references: [id])
children Department[] @relation("DepartmentHierarchy")
@@index([tenantId])
@@map("departments")
@@schema("platform_schema")
}
```
**E. feature_flags 表** P0
```prisma
model FeatureFlag {
id String @id @default(uuid())
featureKey String @unique @map("feature_key") // 功能标识use_gpt_5
displayName String @map("display_name")
description String?
isEnabled Boolean @default(false) @map("is_enabled")
targetRoles String[] @map("target_roles") // 允许的角色列表
targetTenants String[] @default([]) @map("target_tenants") // 允许的租户ID列表
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("feature_flags")
@@schema("platform_schema")
}
```
**F. tenant_modules 表** P0
```prisma
model TenantModule {
id String @id @default(uuid())
tenantId String @map("tenant_id")
moduleCode String @map("module_code") // 模块代码ASL/DC/IIT等
isEnabled Boolean @default(true) @map("is_enabled")
expiresAt DateTime? @map("expires_at") // 模块订阅到期时间
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
@@unique([tenantId, moduleCode])
@@map("tenant_modules")
@@schema("platform_schema")
}
```
**G. tenant_quota_allocations 表** P0🆕 采纳反馈:精细化配额分配
```prisma
model TenantQuotaAllocation {
id Int @id @default(autoincrement())
tenantId String @map("tenant_id")
targetType String @map("target_type") // 'DEPARTMENT' | 'USER'
targetKey String @map("target_key") // DepartmentID 或 UserID
limitAmount BigInt @map("limit_amount") // 分配的Token额度
usedAmount BigInt @default(0) @map("used_amount") // 已使用
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([tenantId, targetType, targetKey])
@@index([tenantId])
@@index([targetType, targetKey])
@@map("tenant_quota_allocations")
@@schema("platform_schema")
}
```
**用途:** 医院端可以将总Token额度分配给"心内科"Department或"张医生"User
#### **7.1.2 admin_schema运营管理**
**H. admin_operation_logs 表** P1 采纳反馈增加module字段
```prisma
model AdminOperationLog {
id Int @id @default(autoincrement())
adminId String @map("admin_id")
operationType String @map("operation_type") // CREATE_TENANT/UPDATE_FEATURE_FLAG等
targetType String @map("target_type") // tenant/user/config
targetId String @map("target_id")
module String? @map("module") // 🆕 所属模块IIT/ASL/系统配置等)
beforeData Json? @map("before_data")
afterData Json? @map("after_data")
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
createdAt DateTime @default(now())
@@index([adminId])
@@index([operationType])
@@index([module]) // 🆕 药企端按模块查询
@@index([createdAt])
@@map("admin_operation_logs")
@@schema("admin_schema")
}
```
### 7.2 后端API架构设计
#### **A. 认证系统** P0
**路由:** `/api/v1/auth/*`
```typescript
// backend/src/platform/auth/routes.ts
POST /api/v1/auth/register // 用户注册
POST /api/v1/auth/login // 登录返回JWT
POST /api/v1/auth/logout // 登出
POST /api/v1/auth/refresh // 刷新Token
GET /api/v1/auth/me // 获取当前用户信息
```
**JWT Payload结构**
```typescript
interface JWTPayload {
userId: string
email: string
role: UserRole
tenantId?: string
tenantType?: TenantType
exp: number // 过期时间
}
```
#### **B. 认证中间件** P0
```typescript
// backend/src/common/middleware/auth.ts
/**
* JWT认证中间件
* 验证Token并将用户信息挂载到 req.user
*/
export const requireAuth = async (req, res, next) => {
const token = extractToken(req)
if (!token) return res.status(401).send({ error: 'Unauthorized' })
try {
const payload = verifyJWT(token)
req.user = await prisma.user.findUnique({ where: { id: payload.userId } })
if (!req.user) return res.status(401).send({ error: 'User not found' })
next()
} catch (error) {
return res.status(401).send({ error: 'Invalid token' })
}
}
/**
* 角色权限中间件
* 检查用户是否具有指定角色
*/
export const requireRole = (...allowedRoles: UserRole[]) => {
return (req, res, next) => {
if (!req.user) return res.status(401).send({ error: 'Unauthorized' })
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).send({ error: 'Forbidden' })
}
next()
}
}
/**
* 租户数据隔离中间件
* 确保用户只能访问自己租户的数据
*/
export const requireTenantAccess = (req, res, next) => {
if (!req.user) return res.status(401).send({ error: 'Unauthorized' })
// SUPER_ADMIN可以访问所有租户数据
if (req.user.role === 'SUPER_ADMIN') return next()
// 其他用户只能访问自己租户的数据
req.tenantId = req.user.tenantId
next()
}
```
#### **C. 运营管理端API** P0
**路由:** `/api/v1/admin/*`
```typescript
// 租户管理
POST /api/v1/admin/tenants // 创建租户
GET /api/v1/admin/tenants // 租户列表
GET /api/v1/admin/tenants/:id // 租户详情
PUT /api/v1/admin/tenants/:id // 更新租户
DELETE /api/v1/admin/tenants/:id // 删除租户
// Feature Flag管理
GET /api/v1/admin/feature-flags // 获取所有Feature Flag
PUT /api/v1/admin/feature-flags/:key // 更新Feature Flag
// 用户管理
GET /api/v1/admin/users // 全局用户列表
POST /api/v1/admin/users/:id/assign-tenant // 分配租户
```
**权限要求:** 全部需要 `requireRole('SUPER_ADMIN')`
#### **D. 机构管理端API** P1
**路由:** `/api/v1/org/*`
```typescript
// 医院管理端
GET /api/v1/org/hospital/departments // 科室列表
POST /api/v1/org/hospital/departments // 创建科室
GET /api/v1/org/hospital/members // 成员列表
POST /api/v1/org/hospital/members/import // 批量导入成员
// 药企管理端
GET /api/v1/org/pharma/projects // 项目列表
GET /api/v1/org/pharma/audit-logs // 审计日志
```
**权限要求:** `requireRole('TENANT_ADMIN')` + `requireTenantAccess`
#### **E. 公开API** P0
**路由:** `/api/public/*`
```typescript
// 租户品牌配置(无需登录)
GET /api/public/tenant-config?code={code} // 获取租户品牌配置
```
### 7.3 前端架构设计
#### **A. 认证模块** P0
**目录结构:**
```
frontend-v2/src/modules/auth/
├── pages/
│ ├── LoginPage.tsx # 通用登录页
│ ├── TenantLoginPage.tsx # 租户专属登录页(动态品牌)
│ └── RegisterPage.tsx # 注册页
├── api/
│ └── authApi.ts # 认证API调用
├── hooks/
│ └── useAuth.ts # 认证Hook
└── routes.tsx # 认证路由
```
#### **B. 运营管理端模块** P0
**目录结构:**
```
frontend-v2/src/modules/admin/
├── pages/
│ ├── Dashboard.tsx # 运营仪表盘
│ ├── TenantManagement/ # 租户管理
│ │ ├── TenantList.tsx
│ │ ├── TenantCreate.tsx
│ │ ├── TenantEdit.tsx
│ │ └── BrandingConfig.tsx # 品牌配置
│ ├── FeatureFlagManagement.tsx # Feature Flag管理
│ └── UserManagement.tsx # 用户管理
├── api/
│ └── adminApi.ts
└── index.tsx # 模块定义
```
**模块注册:**
```typescript
const AdminModule: ModuleDefinition = {
id: 'admin',
name: '运营管理',
path: '/admin',
requiredVersion: undefined, // 不基于version检查
requireRole: ['SUPER_ADMIN'], // 🆕 基于角色检查
component: lazy(() => import('./layouts/AdminLayout')),
}
```
#### **C. 机构管理端模块** P1
**目录结构:**
```
frontend-v2/src/modules/org/
├── hospital/ # 医院管理端
│ ├── pages/
│ │ ├── Dashboard.tsx
│ │ ├── DepartmentManagement.tsx
│ │ └── MemberManagement.tsx
│ └── index.tsx
└── pharma/ # 药企管理端
├── pages/
│ ├── Dashboard.tsx
│ ├── ProjectManagement.tsx
│ └── AuditLogs.tsx
└── index.tsx
```
---
## 8. 实施路线图
### 8.1 Phase 0准备工作1天
**目标:** 统一数据库表结构,清理历史遗留
- [ ] **任务1** 决策保留 `platform_schema.users` 还是 `public.users`
- 建议:保留 `platform_schema.users`(新架构)
- 迁移 `public.users` 的历史数据到 `platform_schema.users`
- 删除 `public.users`
- [ ] **任务2** 创建迁移文档
- 梳理所有业务模块对 User 表的引用
- 制定数据迁移脚本
### 8.2 Phase 1数据库Schema设计2天
**P0 核心表:**
- [ ] **Day 1上午** 设计并创建 `tenants`
- [ ] **Day 1下午** 扩展 `users` 表(增加 tenantId, departmentId, role enum
- [ ] **Day 2上午** 创建 `tenant_users`, `feature_flags`, `tenant_modules`
- [ ] **Day 2下午** 创建 Prisma Schema 并运行迁移
**交付物:**
- ✅ Prisma Schema完整定义
- ✅ 迁移脚本运行通过
- ✅ 测试数据插入验证
### 8.3 Phase 2后端认证系统3天
- [ ] **Day 1** 实现JWT工具类
- `generateToken()`
- `verifyToken()`
- `refreshToken()`
- [ ] **Day 2** 实现认证API
- POST `/api/v1/auth/register`
- POST `/api/v1/auth/login`
- GET `/api/v1/auth/me`
- [ ] **Day 3** 实现认证中间件
- `requireAuth`
- `requireRole`
- `requireTenantAccess`
- 应用到现有Legacy API
**交付物:**
- ✅ 完整的认证系统
- ✅ 所有API加上认证保护
- ✅ Postman测试通过
### 8.4 Phase 3前端认证对接2天
- [ ] **Day 1** 实现登录页面
- LoginPage.tsx
- useAuth Hook
- Token存储localStorage
- [ ] **Day 2** 对接权限框架
- 替换PermissionContext中的mock数据
- 实现从后端获取用户信息
- 实现登出功能
**交付物:**
- ✅ 可用的登录/登出流程
- ✅ 前端权限控制生效
### 8.5 Phase 4运营管理端MVP5天
**P0 核心功能:**
- [ ] **Day 1-2** 租户管理
- 租户列表页
- 创建租户表单(基本信息+租户类型)
- 租户详情页
- [ ] **Day 3** 品牌配置
- Logo上传到OSS
- 登录页背景图上传
- 配置预览
- [ ] **Day 4** Feature Flag管理
- Feature Flag列表
- 开关切换
- 目标租户配置
- [ ] **Day 5** 集成测试
- 运营端完整流程测试
- 权限控制测试
**交付物:**
- ✅ 运营管理端MVP可用
- ✅ 可以创建租户
- ✅ 可以配置品牌
- ✅ 可以管理Feature Flag
### 8.6 Phase 5租户专属登录2天
- [ ] **Day 1** 实现TenantLoginPage
- 动态加载租户品牌配置
- 替换Logo和背景图
- 动态主题色
- [ ] **Day 2** 实现智能路由分发
- 登录后根据role+tenantType跳转
- 测试不同角色的跳转逻辑
**交付物:**
- ✅ 租户专属登录页可用
- ✅ URL`/t/{code}/login` 生效
### 8.7 Phase 6机构管理端按需开发
**P1 功能(后续排期):**
- [ ] 医院管理端:科室管理、成员管理、配额分配
- [ ] 药企管理端:项目监控、审计日志
---
## 9. 关键决策点
### 9.1 技术决策
| 决策点 | 选项 | 建议 | 理由 |
|--------|------|------|------|
| **User表选择** | platform_schema.users vs public.users | ✅ platform_schema.users | 符合新架构Schema隔离清晰 |
| **JWT库选择** | jsonwebtoken vs jose | ✅ jsonwebtoken | 成熟稳定,社区活跃 |
| **密码加密** | bcrypt vs argon2 | ✅ bcrypt | 项目已有依赖见IIT模块 |
| **Token存储** | localStorage vs httpOnly Cookie | ✅ localStorage | 前后端分离,跨域友好 |
| **角色定义方式** | 字符串 vs Enum | ✅ Prisma Enum | 类型安全,避免拼写错误 |
### 9.2 业务决策
| 决策点 | 选项 | 建议 | 理由 |
|--------|------|------|------|
| **租户代码唯一性** | 全局唯一 vs 类型内唯一 | ✅ 全局唯一 | URL `/t/{code}` 需全局唯一 |
| **部门树层级** | 固定2层 vs 无限层级 | ✅ 无限层级 | 支持复杂组织架构 |
| **Feature Flag粒度** | 租户级 vs 用户级 | ✅ 租户级 | 符合商业模式,管理简单 |
---
## 10. 风险与挑战
### 10.1 技术风险
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| **现有API无认证** | 需要全面改造 | 渐进式加入认证优先保护敏感API |
| **两个User表** | 数据不一致 | 尽快统一,编写迁移脚本 |
| **租户数据隔离** | 可能泄露数据 | 严格测试 `requireTenantAccess` 中间件 |
### 10.2 开发挑战
| 挑战 | 难度 | 应对方案 |
|------|------|---------|
| **JWT认证系统** | 中等 | 参考ODJ/BYSY项目实现 |
| **品牌动态加载** | 中等 | 使用CSS变量+OSS图片URL |
| **智能路由分发** | 简单 | 基于role+tenantType的if-else |
---
## 11. 开发资源需求
### 11.1 人力需求
- **后端开发:** 1人 × 10天Phase 0-3
- **前端开发:** 1人 × 7天Phase 3-5
- **测试:** 0.5人 × 3天集成测试
**总计:** 约20人天约3周
### 11.2 技术依赖
**新增npm包**
```json
{
"dependencies": {
"jsonwebtoken": "^9.0.0",
"bcryptjs": "^2.4.3"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.0",
"@types/bcryptjs": "^2.4.2"
}
}
```
---
## 12. 总结
### 12.1 核心结论
1. **当前系统完全没有认证/授权系统**
2. **数据库有两个User表需统一** ⚠️
3. **前端权限框架设计完善但仅为mock** ⚠️
4. **租户体系完全缺失,需从零开发**
### 12.2 优先级建议
**P0Week 1-2** 搭建基础架构
- 数据库Schema设计 + 迁移
- JWT认证系统
- 认证中间件
- 登录/登出功能
- 运营管理端MVP租户管理+品牌配置)
**P1Week 3-4** 完善核心功能
- Feature Flag管理
- 租户专属登录页
- 机构管理端(医院版)
**P2Week 5+** 扩展功能
- 机构管理端(药企版)
- 高级审计日志
- 权限细粒度控制
### 12.3 下一步行动
1.**Review本报告**,确认技术方案
2.**确定开发排期**建议3周冲刺
3.**启动Phase 0**数据库表统一和Schema设计
4.**并行启动前端登录页设计**UI设计师
---
## 9. 🆕 Prompt管理系统整合
### 9.1 为什么Prompt管理是运营管理端的灵魂
根据《02-通用能力层_03-Prompt管理系统与灰度预览设计方案.md》**Prompt管理不是可选功能而是运营管理端存在的核心理由之一**。
#### **业务痛点:**
1. **测试环境无法模拟真实数据**
- ASL的文献筛选需要20篇真实医学论文验证准确率
- DC的数据清洗需要真实病历数据验证抽取效果
- 测试环境的假数据完全无法暴露Prompt的真实问题
2. **当前开发流程效率极低**
- 每次调整Prompt需要改代码 → commit → 部署 → 等待SAE重启约5分钟
- 临床专家无法参与调试(他们不会写代码)
- 无法快速迭代,一天只能尝试几次
3. **生产事故风险高**
- 一旦Prompt发版所有用户立即受影响
- 没有灰度机制,无法小范围验证
#### **解决方案:生产环境灰度预览**
调试者开启Debug模式后自动路由到DRAFT版Prompt验证通过后一键发布为ACTIVE版。
### 9.2 Prompt管理系统架构
#### **9.2.1 数据库设计capability_schema**
```prisma
// --- Prompt Management System ---
model PromptTemplate {
id Int @id @default(autoincrement())
code String @unique // 唯一标识: 'ASL_SCREENING_TitleAbstract'
name String // 人类可读名称
module String // 所属模块: ASL, DC, AIA, IIT
description String?
variables Json? // 预期变量: ["title", "abstract"]
versions PromptVersion[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("prompt_templates")
@@schema("capability_schema")
}
model PromptVersion {
id Int @id @default(autoincrement())
templateId Int @map("template_id")
version Int // 版本号 1, 2, 3...
content String @db.Text // Prompt内容支持Handlebars模板
modelConfig Json? // {"temperature": 0.1, "model": "deepseek-chat"}
status PromptStatus @default(DRAFT)
changelog String? // 修改说明
createdBy String? @map("created_by") // 🔍 审计:谁修改的
template PromptTemplate @relation(fields: [templateId], references: [id])
createdAt DateTime @default(now()) @map("created_at")
@@map("prompt_versions")
@@schema("capability_schema")
@@index([templateId, status]) // 高频查询优化
}
enum PromptStatus {
DRAFT // 草稿仅Debug模式可见
ACTIVE // 线上生效(默认)
ARCHIVED // 归档
@@schema("capability_schema")
}
```
#### **9.2.2 新增角色与权限**
| 角色 | 权限 Code | 说明 |
|------|-----------|------|
| **SUPER_ADMIN** | prompt:\* | 超级管理员拥有所有权限 |
| **PROMPT_ENGINEER** 🆕 | prompt:view<br>prompt:edit<br>prompt:debug<br>prompt:publish | 🎯 **核心角色**专业Prompt工程师或临床专家 |
| HOSPITAL_ADMIN | - | 机构管理员无Prompt权限 |
| PHARMA_ADMIN | - | 药企管理员无Prompt权限 |
**权限详解:**
- `prompt:view` - 查看Prompt列表和历史版本
- `prompt:edit` - 创建/修改DRAFT版本
- `prompt:debug` - ⭐ 核心:开启调试模式
- `prompt:publish` - 发布DRAFT为ACTIVE
#### **9.2.3 核心技术实现**
**A. PromptService后端**
```typescript
// backend/src/common/capabilities/prompt/prompt.service.ts
export class PromptService {
private debugUsers = new Set<string>(); // 内存存储调试用户
private activeCache = new Map<string, string>(); // ACTIVE版本缓存
/**
* 设置调试模式
* @requires Permission: prompt:debug
*/
async setDebugMode(userId: string, enabled: boolean) {
if (enabled) {
this.debugUsers.add(userId);
} else {
this.debugUsers.delete(userId);
}
}
/**
* 获取Prompt核心灰度逻辑
*/
async get(code: string, variables: any, userId: string): Promise<string> {
// 1. 检查是否为调试者
if (this.debugUsers.has(userId)) {
// 优先获取DRAFT版本
const draft = await this.getDraftVersion(code);
if (draft) {
return this.render(draft.content, variables);
}
}
// 2. 普通用户或无DRAFT时获取ACTIVE版本
let active = this.activeCache.get(code);
if (!active) {
const version = await prisma.promptVersion.findFirst({
where: {
template: { code },
status: 'ACTIVE'
},
orderBy: { version: 'desc' }
});
active = version?.content || this.getFallback(code);
this.activeCache.set(code, active);
}
return this.render(active, variables);
}
/**
* Postgres LISTEN/NOTIFY 热更新
*/
async initHotReload() {
const client = await pool.connect();
await client.query('LISTEN prompt_update');
client.on('notification', (msg) => {
console.log('[PromptService] Received update:', msg.payload);
this.activeCache.clear(); // 清空缓存
});
}
}
```
**B. API端点设计**
| 方法 | 路径 | 权限 | 描述 |
|------|------|------|------|
| GET | /api/admin/prompts | prompt:view | 获取所有Prompt模板列表 |
| GET | /api/admin/prompts/:id | prompt:view | 获取详情(含历史版本) |
| POST | /api/admin/prompts/draft | prompt:edit | 保存草稿生成新版本status=DRAFT |
| POST | /api/admin/prompts/publish | prompt:publish | 发布版本DRAFT→ACTIVE触发NOTIFY |
| POST | /api/admin/prompts/debug | **prompt:debug** | **开关调试模式** |
**C. 前端全局调试开关**
```tsx
// frontend-v2/src/modules/admin/components/PromptDebugSwitch.tsx
export const PromptDebugSwitch = () => {
const { hasPermission } = usePermission();
const [debugMode, setDebugMode] = useState(false);
// 🔒 权限控制仅prompt:debug权限用户可见
if (!hasPermission('prompt:debug')) {
return null;
}
const handleToggle = async (enabled: boolean) => {
await api.post('/api/admin/prompts/debug', { enabled });
setDebugMode(enabled);
};
return (
<>
<Switch
checked={debugMode}
onChange={handleToggle}
checkedChildren="🐛 调试模式"
unCheckedChildren="生产模式"
/>
{debugMode && (
<Alert
type="warning"
message="⚠️ 调试模式已开启您当前正在使用草稿版DRAFT提示词"
banner
closable={false}
/>
)}
</>
);
};
```
### 9.3 涉及的所有业务模块
根据文档第9节需要Prompt管理的模块
| 模块 | 核心场景 | Prompt复杂度 | 优先级 |
|------|---------|-------------|--------|
| **ASL** | 标题摘要初筛、全文复筛、证据合成 | ⭐⭐⭐⭐⭐ | P0 |
| **DC** | Tool B提取、Tool C清洗、冲突检测 | ⭐⭐⭐⭐⭐ | P0 |
| **IIT** | 质控检查、意图识别、查询生成 | ⭐⭐⭐⭐⭐ | P1 |
| **PKB** | RAG问答、批处理阅读 | ⭐⭐⭐⭐ | P1 |
| **AIA** | 10+智能体、意图识别 | ⭐⭐⭐ | P2 |
| **RVW** | 规范性检查 | ⭐⭐⭐ | P2 |
### 9.4 Prompt管理开发计划
#### **Phase 0: 基础设施2天**
1. 创建`capability_schema`的Prompt相关表
2. 添加`prompt:*`权限到`platform_schema.permissions`
3. 创建`PROMPT_ENGINEER`角色
4. 实现`PromptService`核心逻辑
#### **Phase 1: 运营端MVP3天**
1. 前端管理界面(列表、编辑器、版本历史)
2. 全局调试开关组件
3. 草稿保存/发布功能
#### **Phase 2: 业务模块接入(随业务开发)**
- ASL筛选模块调用`promptService.get()`
- DC数据清洗模块调用`promptService.get()`
- 其他模块按需接入
### 9.5 安全与风控
1. **权限隔离**
- 严格检查`prompt:debug`权限,防止普通用户误入调试模式
- 调试模式状态存储在内存(用户登出自动失效)
2. **审计日志**
- `PromptVersion.createdBy`记录修改人
- `AdminOperationLog`记录发布行为
3. **兜底机制**
- 代码中保留Hardcoded Prompt作为系统级兜底
- 数据库查询失败时返回默认版本
---
## 10. 🆕 反馈采纳说明
### 10.1 采纳的关键建议
基于《02-通用能力层_10-权限体系梳理反馈与修正建议.md》以下建议已整合到本文档
#### ✅ **1. 增加TenantQuotaAllocation表P0**
**原因:** PRD明确要求医院端按科室/个人分配配额
**实施:** 已在7.1.1章节新增`tenant_quota_allocations`
```prisma
model TenantQuotaAllocation {
targetType String // 'DEPARTMENT' | 'USER'
targetKey String // DepartmentID 或 UserID
limitAmount BigInt // 分配的额度
usedAmount BigInt // 已使用
}
```
#### ✅ **2. 表名改为TenantMemberP1**
**原因:** "Member"语义强调组织关系,"User"通常指登录账号实体
**实施:** 已将`TenantUser`改为`TenantMember`
#### ✅ **3. 审计日志增加module字段P1**
**原因:** 药企端需要查询IIT模块专属日志FDA 21 CFR Part 11合规
**实施:** 已在`AdminOperationLog`表增加`module`字段和索引
#### ✅ **4. Prompt工程化权限P0**
**原因:** 运营管理端核心功能
**实施:** 已在第9章完整设计Prompt管理系统
#### ✅ **5. 超级管理员种子数据P0**
**原因:** 否则系统上线后无法进入后台
**实施:** 将在Phase 0实现Prisma Seed脚本
#### ✅ **6. Phase 0回滚方案P0**
**原因:** 数据安全基本原则
**实施:** 迁移脚本将先重命名`public.users``public.users_backup`保留1周
#### ✅ **7. 租户配置API缓存P1**
**原因:** 每个用户打开登录页都会调用,高并发下需要缓存
**实施:** 将在实施时添加`Cache-Control: public, max-age=3600`
### 10.2 后续改进建议
以下建议暂不在MVP阶段实施但列入技术债务清单
#### 🔄 **1. JWT安全性P2**
**建议:** 使用HttpOnly Cookie替代localStorage
**理由:** localStorage容易受XSS攻击
**计划:** 在药企端上线前(需更高安全性)实施
#### 🔄 **2. Prisma Extension多租户隔离P1**
**建议:** 在ORM层强制加入`tenantId`过滤
**理由:** 防止开发人员忘记在Controller层加中间件
**计划:** Phase 2引入
```typescript
const prismaExtended = prisma.$extends({
query: {
$allModels: {
async findMany({ args, query }) {
args.where = { ...args.where, tenantId: currentTenantId };
return query(args);
}
}
}
});
```
### 10.3 反馈质量评价
**总体评估:** 优秀 ⭐⭐⭐⭐⭐
- ✅ 9条建议中7条立即采纳2条纳入后续计划
- ✅ 发现了配额分配模型的设计缺陷Critical
- ✅ 强调了Prompt管理的核心地位
- ✅ 提出了实用的工程实践建议(回滚方案、种子数据)
**结论:** 文档质量高,风险可控,**可以开始开发**。
---
**报告完毕。准备好开始开发了吗?** 🚀