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
1383 lines
40 KiB
Markdown
1383 lines
40 KiB
Markdown
# **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:运营管理端MVP(5天)
|
||
|
||
**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 优先级建议
|
||
|
||
**P0(Week 1-2):** 搭建基础架构
|
||
- 数据库Schema设计 + 迁移
|
||
- JWT认证系统
|
||
- 认证中间件
|
||
- 登录/登出功能
|
||
- 运营管理端MVP(租户管理+品牌配置)
|
||
|
||
**P1(Week 3-4):** 完善核心功能
|
||
- Feature Flag管理
|
||
- 租户专属登录页
|
||
- 机构管理端(医院版)
|
||
|
||
**P2(Week 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: 运营端MVP(3天)**
|
||
|
||
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. 表名改为TenantMember(P1)**
|
||
|
||
**原因:** "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管理的核心地位
|
||
- ✅ 提出了实用的工程实践建议(回滚方案、种子数据)
|
||
|
||
**结论:** 文档质量高,风险可控,**可以开始开发**。
|
||
|
||
---
|
||
|
||
**报告完毕。准备好开始开发了吗?** 🚀
|
||
|