Sprint 1-3 Completed (Backend + Frontend): Backend (Sprint 1-2): - Implement 5-layer Agent framework (Query->Planner->Executor->Tools->Reflection) - Create agent_schema with 6 tables (agent_definitions, stages, prompts, sessions, traces, reflexion_rules) - Create protocol_schema with 2 tables (protocol_contexts, protocol_generations) - Implement Protocol Agent core services (Orchestrator, ContextService, PromptBuilder) - Integrate LLM service adapter (DeepSeek/Qwen/GPT-5/Claude) - 6 API endpoints with full authentication - 10/10 API tests passed Frontend (Sprint 3): - Add Protocol Agent entry in AgentHub (indigo theme card) - Implement ProtocolAgentPage with 3-column layout - Collapsible sidebar (Gemini style, 48px <-> 280px) - StatePanel with 5 stage cards (scientific_question, pico, study_design, sample_size, endpoints) - ChatArea with sync button and action cards integration - 100% prototype design restoration (608 lines CSS) - Detailed endpoints structure: baseline, exposure, outcomes, confounders Features: - 5-stage dialogue flow for research protocol design - Conversation-driven interaction with sync-to-protocol button - Real-time context state management - One-click protocol generation button (UI ready, backend pending) Database: - agent_schema: 6 tables for reusable Agent framework - protocol_schema: 2 tables for Protocol Agent - Seed data: 1 agent + 5 stages + 9 prompts + 4 reflexion rules Code Stats: - Backend: 13 files, 4338 lines - Frontend: 14 files, 2071 lines - Total: 27 files, 6409 lines Status: MVP core functionality completed, pending frontend-backend integration testing Next: Sprint 4 - One-click protocol generation + Word export
6.8 KiB
6.8 KiB
模块认证规范
本文档定义了业务模块如何正确使用平台认证能力,确保所有 API 都正确携带和验证用户身份。
1. 架构概览
┌─────────────────────────────────────────────────────────────┐
│ 前端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ common/api/axios.ts ← 带认证的 axios 实例 │ │
│ │ framework/auth/api.ts ← Token 管理 (getAccessToken)│ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ Authorization: Bearer <token>
▼
┌─────────────────────────────────────────────────────────────┐
│ 后端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ common/auth/auth.middleware.ts │ │
│ │ - authenticate: 验证 JWT Token │ │
│ │ - requirePermission: 权限检查 │ │
│ │ - requireRoles: 角色检查 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2. 前端规范
2.1 使用带认证的 axios 实例(推荐)
// 导入带认证的 apiClient
import apiClient from '../../../common/api/axios';
// 使用方式与 axios 完全相同,自动携带 JWT Token
const response = await apiClient.get('/api/v2/xxx');
const response = await apiClient.post('/api/v2/xxx', data);
2.2 使用原生 fetch(需手动添加 Token)
import { getAccessToken } from '../../../framework/auth/api';
// 创建 getAuthHeaders 函数
function getAuthHeaders(): HeadersInit {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
const token = getAccessToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
// 所有 fetch 请求使用 getAuthHeaders()
const response = await fetch(url, {
headers: getAuthHeaders(),
});
// 文件上传(不设置 Content-Type)
const token = getAccessToken();
const headers: HeadersInit = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, {
method: 'POST',
headers,
body: formData,
});
3. 后端规范
3.1 路由添加认证中间件
// 导入认证中间件
import { authenticate, requirePermission } from '../../../common/auth/auth.middleware.js';
// 添加到路由
fastify.get('/xxx', { preHandler: [authenticate] }, handler);
// 需要特定权限
fastify.post('/xxx', {
preHandler: [authenticate, requirePermission('module:action')]
}, handler);
3.2 控制器获取用户 ID
/**
* 获取用户ID(从JWT Token中获取)
*/
function getUserId(request: FastifyRequest): string {
const userId = (request as any).user?.userId;
if (!userId) {
throw new Error('User not authenticated');
}
return userId;
}
// 在控制器方法中使用
async function myHandler(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
// ... 使用 userId
}
3.3 JWT Token 结构
interface DecodedToken {
userId: string; // 用户ID
phone: string; // 手机号
role: string; // 角色
tenantId: string; // 租户ID
tenantCode?: string; // 租户Code
iat: number; // 签发时间
exp: number; // 过期时间
}
4. 检查清单
4.1 新模块开发检查清单
-
前端 API 文件
- 使用
apiClient或添加getAuthHeaders() - 文件上传单独处理(不设置 Content-Type)
- 导出函数不包含测试用 userId 参数
- 使用
-
后端路由文件
- 导入
authenticate中间件 - 所有需要认证的路由添加
preHandler: [authenticate] - 公开 API(如模板列表)可不添加认证
- 导入
-
后端控制器文件
- 添加
getUserId()辅助函数 - 移除所有
MOCK_USER_ID或硬编码默认值 - 使用
getUserId(request)获取用户 ID
- 添加
4.2 已完成模块状态
| 模块 | 前端 API | 后端路由 | 后端控制器 | 状态 |
|---|---|---|---|---|
| RVW | ✅ apiClient | ✅ authenticate | ✅ getUserId | ✅ |
| PKB | ✅ 拦截器 | ✅ authenticate | ✅ getUserId | ✅ |
| ASL | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
| DC Tool B | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
| DC Tool C | ✅ apiClient | ✅ authenticate | ✅ getUserId | ✅ |
| IIT | N/A (企业微信) | N/A | ✅ 企业微信userId | ✅ |
| Prompt管理 | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
5. 常见错误和解决方案
5.1 401 Unauthorized
原因: 前端没有携带 JWT Token 或 Token 过期
解决:
- 检查前端 API 是否使用
apiClient或getAuthHeaders() - 检查 localStorage 中是否有
accessToken - 如果 Token 过期,尝试刷新或重新登录
5.2 User not authenticated
原因: 后端路由没有添加 authenticate 中间件
解决: 在路由定义中添加 preHandler: [authenticate]
5.3 TypeError: Cannot read property 'userId' of undefined
原因: 使用了错误的属性名(request.user.id 而非 request.user.userId)
解决: 使用 (request as any).user?.userId
6. 参考文件
- 前端 axios 实例:
frontend-v2/src/common/api/axios.ts - 前端 Token 管理:
frontend-v2/src/framework/auth/api.ts - 后端认证中间件:
backend/src/common/auth/auth.middleware.ts - 后端 JWT 服务:
backend/src/common/auth/jwt.service.ts