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

571 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
# 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 获取智能体列表
```http
GET /api/v2/aia/agents
```
**查询参数**
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| stage | string | 否 | 筛选阶段:`design`, `data`, `analysis`, `write`, `publish` |
**响应**
```json
{
"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 获取智能体详情
```http
GET /api/v2/aia/agents/:id
```
**响应**
```json
{
"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 智能意图识别
```http
POST /api/v2/aia/intent/route
```
**请求体**
```json
{
"query": "帮我分析一下这份数据"
}
```
**响应**
```json
{
"code": 0,
"data": {
"agentId": "data-analysis",
"agentName": "统计分析小助手",
"confidence": 0.92,
"prefillPrompt": "请帮我分析这份数据,包括描述性统计和相关性分析"
}
}
```
---
### 3. 对话管理
#### 3.1 获取对话列表
```http
GET /api/v2/aia/conversations
```
**查询参数**
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| agentId | string | 否 | 按智能体筛选 |
| projectId | string | 否 | 按项目筛选NULL 表示通用对话) |
| page | number | 否 | 页码,默认 1 |
| pageSize | number | 否 | 每页数量,默认 20 |
**响应**
```json
{
"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 创建对话
```http
POST /api/v2/aia/conversations
```
**请求体**
```json
{
"agentId": "research-design",
"projectId": null,
"title": "新对话"
}
```
**响应**
```json
{
"code": 0,
"data": {
"id": "conv-002",
"title": "新对话",
"agentId": "research-design",
"projectId": null,
"createdAt": "2026-01-11T14:00:00Z"
}
}
```
#### 3.3 获取对话详情(含历史消息)
```http
GET /api/v2/aia/conversations/:id
```
**查询参数**
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| limit | number | 否 | 获取最近N条消息默认 50 |
**响应**
```json
{
"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 删除对话
```http
DELETE /api/v2/aia/conversations/:id
```
**响应**
```json
{
"code": 0,
"data": {
"deleted": true
}
}
```
---
### 4. 消息发送(流式)
#### 4.1 发送消息并获取流式响应
```http
POST /api/v2/aia/conversations/:id/messages/stream
```
**请求头**
```
Content-Type: application/json
Accept: text/event-stream
```
**请求体**
```json
{
"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 上传附件
```http
POST /api/v2/aia/conversations/:id/attachments
```
**请求头**
```
Content-Type: multipart/form-data
```
**请求体FormData**
| 字段 | 类型 | 必填 | 描述 |
|------|------|------|------|
| file | File | 是 | 文件PDF/Word/TXT/Excel最大20MB |
**响应**
```json
{
"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 获取项目列表
```http
GET /api/v2/aia/projects
```
**响应**
```json
{
"code": 0,
"data": {
"projects": [
{
"id": "proj-001",
"name": "糖尿病研究项目",
"description": "2型糖尿病患者生活方式干预研究",
"conversationCount": 5,
"createdAt": "2026-01-01T10:00:00Z",
"updatedAt": "2026-01-11T12:00:00Z"
}
]
}
}
```
---
## 🔧 错误处理
### 错误响应格式
```json
{
"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附件
```typescript
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消息
```typescript
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
```http
###
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 设计文档 |