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

40 KiB
Raw Blame History

AIclinicalresearch 权限与角色体系梳理报告

文档版本: v1.1整合Prompt管理需求
创建日期: 2026-01-11
最后更新: 2026-01-11
作者: AI架构师
目的: 系统性梳理当前权限实现状况,为运营管理端和机构管理端开发做准备
变更说明: 整合反馈建议 + Prompt管理系统需求


📋 目录

  1. 当前系统状态分析
  2. 数据库层面梳理
  3. 后端权限实现梳理
  4. 前端权限实现梳理
  5. 差距分析
  6. 新PRD需求解读
  7. 架构设计建议
  8. 实施路线图
  9. 🆕 Prompt管理系统整合
  10. 🆕 反馈采纳说明

1. 当前系统状态分析

1.1 核心发现 🔍

已有基础

  • 数据库有基础的User表platform_schema.userspublic.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定义

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 旧表(历史遗留)

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表在publicschema但不完整。

2.3 已有的审计日志

IIT模块的审计日志 (可参考):

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

核心代码:

// ⚠️ 硬编码为最高权限,仅供开发测试
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定义

// framework/permission/types.ts
export type UserVersion = 'free' | 'basic' | 'professional' | 'premium'

4.2 权限检查逻辑 架构完整

模块注册时的权限声明:

// 模块定义接口
interface ModuleDefinition {
  id: string
  name: string
  path: string
  requiredVersion?: UserVersion  // 🎯 权限要求
  // ...
}

路由守卫:

// 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

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

{
  "branding": {
    "logoUrl": "https://oss.../jst_logo.png",
    "loginBackgroundUrl": "https://oss.../jst_bldg.jpg",
    "primaryColor": "#0056b3",
    "welcomeTitle": "北京积水潭医院 AI 临床科研平台",
    "welcomeSubTitle": "智能化 · 规范化 · 高效率"
  }
}

6.4 智能路由分发(登录后跳转)

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

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

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

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

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

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

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🆕 采纳反馈:精细化配额分配

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字段

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/*

// 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结构

interface JWTPayload {
  userId: string
  email: string
  role: UserRole
  tenantId?: string
  tenantType?: TenantType
  exp: number  // 过期时间
}

B. 认证中间件 P0

// 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/*

// 租户管理
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/*

// 医院管理端
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/*

// 租户品牌配置(无需登录)
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                      # 模块定义

模块注册:

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包

{
  "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

// --- 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
prompt:edit
prompt:debug
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后端

// 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. 前端全局调试开关

// 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

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.userspublic.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引入

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管理的核心地位
  • 提出了实用的工程实践建议(回滚方案、种子数据)

结论: 文档质量高,风险可控,可以开始开发


报告完毕。准备好开始开发了吗? 🚀