Files
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

11 KiB
Raw Permalink Blame History

AIA V2.1 后端 API 设计

版本V2.1
创建日期2026-01-11
基础路径/api/v2/aia


📋 API 概览

方法 路径 描述 认证
GET /agents 获取智能体列表
GET /agents/:id 获取智能体详情
POST /intent/route 意图路由
GET /conversations 获取对话列表
POST /conversations 创建对话
GET /conversations/:id 获取对话详情
DELETE /conversations/:id 删除对话
POST /conversations/:id/messages/stream 发送消息(流式)
POST /conversations/:id/attachments 上传附件
GET /projects 获取项目列表
GET /projects/:id 获取项目详情

🔐 认证

所有 API 需要在请求头中携带 JWT Token

Authorization: Bearer <token>

📖 API 详细定义

1. 智能体相关

1.1 获取智能体列表

GET /api/v2/aia/agents

查询参数

参数 类型 必填 描述
stage string 筛选阶段:design, data, analysis, write, publish

响应

{
  "code": 0,
  "data": {
    "agents": [
      {
        "id": "research-design",
        "name": "科研设计小助手",
        "description": "帮助您完成研究方案设计、文献检索、方法学指导",
        "icon": "🔬",
        "stage": "design",
        "color": "#3B82F6",
        "knowledgeBaseId": "kb-001",
        "isTool": false
      },
      {
        "id": "dc-tool",
        "name": "数据采集工具",
        "description": "跳转到数据采集模块",
        "icon": "📊",
        "stage": "data",
        "color": "#8B5CF6",
        "targetModule": "/data-collection",
        "isTool": true
      }
    ]
  }
}

1.2 获取智能体详情

GET /api/v2/aia/agents/:id

响应

{
  "code": 0,
  "data": {
    "id": "research-design",
    "name": "科研设计小助手",
    "description": "帮助您完成研究方案设计、文献检索、方法学指导",
    "icon": "🔬",
    "stage": "design",
    "color": "#3B82F6",
    "knowledgeBaseId": "kb-001",
    "systemPrompt": "你是一个专业的医学科研设计专家...",
    "welcomeMessage": "您好!我是科研设计小助手,我可以帮您:\n- 设计研究方案\n- 检索相关文献\n- 指导研究方法",
    "suggestedQuestions": [
      "如何设计一个RCT研究",
      "帮我检索近5年糖尿病研究文献",
      "什么情况下使用倾向性评分匹配?"
    ]
  }
}

2. 意图路由

2.1 智能意图识别

POST /api/v2/aia/intent/route

请求体

{
  "query": "帮我分析一下这份数据"
}

响应

{
  "code": 0,
  "data": {
    "agentId": "data-analysis",
    "agentName": "统计分析小助手",
    "confidence": 0.92,
    "prefillPrompt": "请帮我分析这份数据,包括描述性统计和相关性分析"
  }
}

3. 对话管理

3.1 获取对话列表

GET /api/v2/aia/conversations

查询参数

参数 类型 必填 描述
agentId string 按智能体筛选
projectId string 按项目筛选NULL 表示通用对话)
page number 页码,默认 1
pageSize number 每页数量,默认 20

响应

{
  "code": 0,
  "data": {
    "conversations": [
      {
        "id": "conv-001",
        "title": "RCT研究设计咨询",
        "agentId": "research-design",
        "agentName": "科研设计小助手",
        "projectId": null,
        "messageCount": 12,
        "lastMessage": "好的,我来帮您设计研究方案...",
        "createdAt": "2026-01-11T10:00:00Z",
        "updatedAt": "2026-01-11T12:30:00Z"
      }
    ],
    "pagination": {
      "total": 25,
      "page": 1,
      "pageSize": 20,
      "totalPages": 2
    }
  }
}

3.2 创建对话

POST /api/v2/aia/conversations

请求体

{
  "agentId": "research-design",
  "projectId": null,
  "title": "新对话"
}

响应

{
  "code": 0,
  "data": {
    "id": "conv-002",
    "title": "新对话",
    "agentId": "research-design",
    "projectId": null,
    "createdAt": "2026-01-11T14:00:00Z"
  }
}

3.3 获取对话详情(含历史消息)

GET /api/v2/aia/conversations/:id

查询参数

参数 类型 必填 描述
limit number 获取最近N条消息默认 50

响应

{
  "code": 0,
  "data": {
    "id": "conv-001",
    "title": "RCT研究设计咨询",
    "agentId": "research-design",
    "agentName": "科研设计小助手",
    "projectId": null,
    "createdAt": "2026-01-11T10:00:00Z",
    "updatedAt": "2026-01-11T12:30:00Z",
    "messages": [
      {
        "id": "msg-001",
        "role": "user",
        "content": "帮我设计一个关于糖尿病的RCT研究",
        "attachments": [],
        "createdAt": "2026-01-11T10:00:00Z"
      },
      {
        "id": "msg-002",
        "role": "assistant",
        "content": "好的我来帮您设计一个糖尿病RCT研究方案...",
        "thinkingContent": "用户想设计RCT研究需要考虑1研究目的 2入排标准 3样本量 4随机化方法 5盲法 6结局指标...",
        "model": "deepseek-v3",
        "tokens": 1250,
        "createdAt": "2026-01-11T10:00:30Z"
      }
    ]
  }
}

3.4 删除对话

DELETE /api/v2/aia/conversations/:id

响应

{
  "code": 0,
  "data": {
    "deleted": true
  }
}

4. 消息发送(流式)

4.1 发送消息并获取流式响应

POST /api/v2/aia/conversations/:id/messages/stream

请求头

Content-Type: application/json
Accept: text/event-stream

请求体

{
  "content": "帮我分析这份数据",
  "attachmentIds": ["att-001", "att-002"],
  "enableDeepThinking": true
}

响应SSE 格式)

event: thinking_start
data: {}

event: thinking_delta
data: {"content": "用户上传了数据文件,需要"}

event: thinking_delta
data: {"content": "进行描述性统计分析..."}

event: thinking_end
data: {"duration": 3200}

event: message_start
data: {"id": "msg-003"}

event: delta
data: {"content": "根据您上传的数据,"}

event: delta
data: {"content": "我来为您进行分析..."}

event: message_end
data: {"id": "msg-003", "tokens": 850, "model": "deepseek-v3"}

event: done
data: {}

SSE 事件类型

事件 描述 数据格式
thinking_start 开始深度思考 {}
thinking_delta 思考内容片段 {"content": "..."}
thinking_end 思考结束 {"duration": number}
message_start 开始生成回复 {"id": "..."}
delta 回复内容片段 {"content": "..."}
message_end 回复结束 {"id": "...", "tokens": number, "model": "..."}
error 发生错误 {"code": "...", "message": "..."}
done 流结束 {}

5. 附件上传

5.1 上传附件

POST /api/v2/aia/conversations/:id/attachments

请求头

Content-Type: multipart/form-data

请求体FormData

字段 类型 必填 描述
file File 文件PDF/Word/TXT/Excel最大20MB

响应

{
  "code": 0,
  "data": {
    "id": "att-001",
    "filename": "研究数据.xlsx",
    "mimeType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "size": 1024000,
    "ossUrl": "https://oss.example.com/attachments/att-001.xlsx",
    "textExtracted": true,
    "tokenCount": 15000,
    "truncated": false,
    "createdAt": "2026-01-11T14:00:00Z"
  }
}

错误码

错误码 描述
ATTACHMENT_TOO_LARGE 文件超过 20MB
ATTACHMENT_TYPE_NOT_SUPPORTED 不支持的文件类型
ATTACHMENT_LIMIT_EXCEEDED 附件数量超过上限5个
TEXT_EXTRACTION_FAILED 文本提取失败

6. 项目管理

6.1 获取项目列表

GET /api/v2/aia/projects

响应

{
  "code": 0,
  "data": {
    "projects": [
      {
        "id": "proj-001",
        "name": "糖尿病研究项目",
        "description": "2型糖尿病患者生活方式干预研究",
        "conversationCount": 5,
        "createdAt": "2026-01-01T10:00:00Z",
        "updatedAt": "2026-01-11T12:00:00Z"
      }
    ]
  }
}

🔧 错误处理

错误响应格式

{
  "code": -1,
  "error": {
    "code": "CONVERSATION_NOT_FOUND",
    "message": "对话不存在"
  }
}

通用错误码

错误码 HTTP 状态码 描述
UNAUTHORIZED 401 未授权
FORBIDDEN 403 无权限
NOT_FOUND 404 资源不存在
VALIDATION_ERROR 400 参数验证失败
INTERNAL_ERROR 500 服务器内部错误
LLM_ERROR 500 LLM 调用失败
RATE_LIMITED 429 请求过于频繁

📊 数据模型

Attachment附件

interface Attachment {
  id: string;              // 附件ID
  filename: string;        // 原始文件名
  mimeType: string;        // MIME 类型
  size: number;            // 文件大小(字节)
  ossUrl: string;          // OSS 存储地址
  textContent?: string;    // 提取的文本内容(存储时截断)
  tokenCount: number;      // 文本 Token 数
  truncated: boolean;      // 是否被截断超过30K tokens
  createdAt: string;       // 创建时间
}

Message消息

interface Message {
  id: string;
  conversationId: string;
  role: 'user' | 'assistant';
  content: string;
  thinkingContent?: string;  // 深度思考内容
  attachments?: Attachment[];
  model?: string;
  tokens?: number;
  isPinned: boolean;
  createdAt: string;
}

🧪 测试示例REST Client

### 获取智能体列表
GET {{baseUrl}}/api/v2/aia/agents
Authorization: Bearer {{token}}

### 意图路由
POST {{baseUrl}}/api/v2/aia/intent/route
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "query": "帮我分析数据"
}

### 创建对话
POST {{baseUrl}}/api/v2/aia/conversations
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "agentId": "research-design",
  "title": "测试对话"
}

### 发送消息(流式)
POST {{baseUrl}}/api/v2/aia/conversations/{{conversationId}}/messages/stream
Authorization: Bearer {{token}}
Content-Type: application/json
Accept: text/event-stream

{
  "content": "帮我设计一个RCT研究",
  "enableDeepThinking": true
}

### 上传附件
POST {{baseUrl}}/api/v2/aia/conversations/{{conversationId}}/attachments
Authorization: Bearer {{token}}
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="data.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

< ./data.xlsx
------WebKitFormBoundary--

📝 更新日志

日期 版本 内容
2026-01-11 V1.0 创建 API 设计文档