forked from xiaohei/taiji-AI-PAD
更新渠道删除
This commit is contained in:
+171
-8
@@ -2040,7 +2040,89 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
---
|
||||
|
||||
#### 4. 统一管理渠道资源
|
||||
#### 4. 更新渠道信息
|
||||
|
||||
**PUT** `/api/admin/channels/{channel_id}`
|
||||
|
||||
更新渠道的基本信息。
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "合作渠道A(更新)",
|
||||
"email": "new-email@channel-a.com",
|
||||
"commissionRate": 12.0,
|
||||
"status": "active"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
- `name` (string, 可选): 渠道名称
|
||||
- `email` (string, 可选): 渠道管理员邮箱
|
||||
- `commissionRate` (float, 可选): 佣金比例,0-100之间
|
||||
- `status` (string, 可选): 状态,`active` 或 `inactive`
|
||||
|
||||
**curl示例**:
|
||||
```bash
|
||||
curl -X PUT "http://localhost:8002/api/admin/channels/channel-uuid-1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-d '{
|
||||
"name": "合作渠道A(更新)",
|
||||
"commissionRate": 12.0
|
||||
}'
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "channel-uuid-1",
|
||||
"name": "合作渠道A(更新)",
|
||||
"email": "partner@channel-a.com",
|
||||
"commissionRate": 12.0,
|
||||
"status": "active"
|
||||
},
|
||||
"message": "渠道信息更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. 删除渠道
|
||||
|
||||
**DELETE** `/api/admin/channels/{channel_id}`
|
||||
|
||||
删除渠道(软删除,仅标记为不活跃)。如果渠道下有活跃租户,将拒绝删除。
|
||||
|
||||
**curl示例**:
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8002/api/admin/channels/channel-uuid-1" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "channel-uuid-1"
|
||||
},
|
||||
"message": "渠道已删除"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应(有关联租户)**:
|
||||
```json
|
||||
{
|
||||
"detail": "渠道下有 5 个活跃租户,无法删除。请先移除或停用所有租户。"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. 统一管理渠道资源
|
||||
|
||||
**PUT** `/api/admin/channels/{channel_id}/resources`
|
||||
|
||||
@@ -2074,7 +2156,7 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
### 申请审批相关
|
||||
|
||||
#### 5. 获取所有申请
|
||||
#### 7. 获取所有申请
|
||||
|
||||
**GET** `/api/admin/channels/applications`
|
||||
|
||||
@@ -2105,7 +2187,7 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
---
|
||||
|
||||
#### 6. 审批申请
|
||||
#### 8. 审批申请
|
||||
|
||||
**PUT** `/api/admin/channels/applications/{application_id}/review`
|
||||
|
||||
@@ -2129,7 +2211,7 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
### 资源管理相关
|
||||
|
||||
#### 7. 获取所有模型供应商
|
||||
#### 9. 获取所有模型供应商
|
||||
|
||||
**GET** `/api/admin/resources/models`
|
||||
|
||||
@@ -2157,7 +2239,7 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
---
|
||||
|
||||
#### 8. 获取所有Agent资源
|
||||
#### 10. 获取所有Agent资源
|
||||
|
||||
**GET** `/api/admin/resources/agents`
|
||||
|
||||
@@ -2183,9 +2265,84 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
---
|
||||
|
||||
#### 11. 删除Agent资源
|
||||
|
||||
**DELETE** `/api/admin/resources/agents/{agent_id}`
|
||||
|
||||
删除Agent资源(软删除,仅标记为不活跃)。
|
||||
|
||||
**curl示例**:
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8002/api/admin/resources/agents/agent-uuid-1" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "agent-uuid-1",
|
||||
"name": "weather-agent"
|
||||
},
|
||||
"message": "Agent资源已删除"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 12. 更新Agent资源配置
|
||||
|
||||
**PUT** `/api/admin/resources/agents/{agent_id}/config`
|
||||
|
||||
更新Agent的资源配置(CPU、内存、最大实例数)。
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"cpu": 4.0,
|
||||
"memory": 8.0,
|
||||
"maxInstances": 10
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
- `cpu` (float, 可选): CPU核心数,0.1-64之间
|
||||
- `memory` (float, 可选): 内存大小(GB),0.5-256之间
|
||||
- `maxInstances` (int, 可选): 最大实例数,1-1000之间
|
||||
|
||||
**curl示例**:
|
||||
```bash
|
||||
curl -X PUT "http://localhost:8002/api/admin/resources/agents/agent-uuid-1/config" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-d '{
|
||||
"cpu": 4.0,
|
||||
"memory": 8.0,
|
||||
"maxInstances": 10
|
||||
}'
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "agent-uuid-1",
|
||||
"name": "weather-agent",
|
||||
"cpu": 4.0,
|
||||
"memory": 8.0,
|
||||
"maxInstances": 10
|
||||
},
|
||||
"message": "Agent资源配置更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 监控相关
|
||||
|
||||
#### 9. 监控Agent健康状态
|
||||
#### 13. 监控Agent健康状态
|
||||
|
||||
**GET** `/api/admin/monitoring/agents`
|
||||
|
||||
@@ -2214,7 +2371,7 @@ curl -s -X POST "http://localhost:8002/api/admin/channels/create" \
|
||||
|
||||
### 计费相关(三维度)
|
||||
|
||||
#### 10. 获取三维度计费统计
|
||||
#### 14. 获取三维度计费统计
|
||||
|
||||
**GET** `/api/admin/billing/overview`
|
||||
|
||||
@@ -2520,7 +2677,7 @@ def get_dashboard(token: str):
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: v2.2.0
|
||||
**文档版本**: v2.3.0
|
||||
**最后更新**: 2025年12月25日
|
||||
**维护者**: taiji-AI-PAD 项目组
|
||||
|
||||
@@ -2647,6 +2804,12 @@ echo "=== API测试完成 ==="
|
||||
|
||||
## 更新日志
|
||||
|
||||
- **v2.3.0** (2025-12-25): **新增管理接口**
|
||||
- ✅ 添加 PUT /api/admin/channels/{channel_id} - 更新渠道信息
|
||||
- ✅ 添加 DELETE /api/admin/channels/{channel_id} - 删除渠道(软删除)
|
||||
- ✅ 添加 DELETE /api/admin/resources/agents/{agent_id} - 删除Agent资源(软删除)
|
||||
- ✅ 添加 PUT /api/admin/resources/agents/{agent_id}/config - 更新Agent资源配置
|
||||
- ✅ 更新超级管理员API接口编号
|
||||
- **v2.2.0** (2025-12-25): **完整API测试验证**
|
||||
- ✅ 添加完整API测试流程示例
|
||||
- ✅ 更新所有curl命令示例
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
# Taiji AI-PAD 全面 API 测试报告
|
||||
|
||||
**测试日期**: 2025年12月25日
|
||||
**测试版本**: v2.1.0
|
||||
**测试环境**: Docker Compose 部署环境
|
||||
|
||||
---
|
||||
|
||||
## 📊 测试总结
|
||||
|
||||
| 指标 | 结果 |
|
||||
|------|------|
|
||||
| **总测试数** | 40 |
|
||||
| **通过测试** | 40 |
|
||||
| **失败测试** | 0 |
|
||||
| **成功率** | **100%** ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 修复的问题
|
||||
|
||||
### 1. Agent创建时数据库约束错误
|
||||
**问题**: 创建Agent时,测试用户缺少必需的`name`字段,导致数据库NOT NULL约束违反。
|
||||
|
||||
**修复**:
|
||||
- 在`_ensure_test_user`函数中添加`name`、`password_hash`和`role`字段
|
||||
- 文件: `services/mcp-server/app/routes/agents.py`
|
||||
|
||||
```python
|
||||
test_user = User(
|
||||
id=TEST_USER_ID,
|
||||
name="测试用户", # 添加name字段
|
||||
username="test_user",
|
||||
email="test@taiji-ai.com",
|
||||
password_hash="", # 添加password_hash字段
|
||||
hashed_password="",
|
||||
full_name="测试用户",
|
||||
role="user", # 添加role字段
|
||||
is_active=True,
|
||||
is_admin=False,
|
||||
)
|
||||
```
|
||||
|
||||
### 2. 渠道租户列表获取channelId失败
|
||||
**问题**: 渠道管理员登录时,JWT token中缺少`channelId`字段,导致无法获取租户列表。
|
||||
|
||||
**修复**:
|
||||
- 在渠道登录时自动创建或关联渠道
|
||||
- 在JWT token中包含`channelId`
|
||||
- 文件: `services/mcp-server/app/routes/frontend_integration.py`
|
||||
|
||||
```python
|
||||
# 如果用户没有channel_id,查找或创建对应的渠道
|
||||
channel_id = user.channel_id
|
||||
if not channel_id:
|
||||
result = await db.execute(select(Channel).where(Channel.email == email))
|
||||
channel = result.scalar_one_or_none()
|
||||
|
||||
if not channel:
|
||||
# 创建一个默认渠道
|
||||
channel = Channel(
|
||||
name=f"渠道-{email.split('@')[0]}",
|
||||
email=email,
|
||||
password_hash=user.password_hash,
|
||||
commission_rate=10.0,
|
||||
channel_credit=0.0,
|
||||
custom_agent_cpu=2.0,
|
||||
custom_agent_memory=4.0,
|
||||
status="active"
|
||||
)
|
||||
db.add(channel)
|
||||
await db.flush()
|
||||
|
||||
user.channel_id = channel.id
|
||||
await db.commit()
|
||||
channel_id = channel.id
|
||||
|
||||
token = create_access_token({
|
||||
"sub": str(user.id),
|
||||
"email": email,
|
||||
"role": "channel_admin",
|
||||
"channelId": str(channel_id) # 包含channelId
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Agent工具执行时execution_id重复
|
||||
**问题**: 测试脚本使用固定的request id,导致execution_id重复,违反数据库唯一约束。
|
||||
|
||||
**修复**:
|
||||
- 在测试脚本中使用时间戳生成唯一的request id
|
||||
- 文件: `scripts/test_all_apis.py`
|
||||
|
||||
```python
|
||||
execution_data = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": f"test-{int(time.time()*1000)}", # 使用时间戳生成唯一ID
|
||||
"method": "tools/call",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Prometheus指标返回格式
|
||||
**说明**: Prometheus指标端点返回纯文本格式(不是JSON),这是正常的。
|
||||
|
||||
**修复**:
|
||||
- 在测试脚本中添加`expect_json=False`参数
|
||||
- 正确处理非JSON响应
|
||||
|
||||
---
|
||||
|
||||
## 📋 测试覆盖范围
|
||||
|
||||
### 1. Data Ingestion 服务 (6项测试)
|
||||
- ✅ 健康检查
|
||||
- ✅ 获取统计信息
|
||||
- ✅ 获取工具列表
|
||||
- ✅ 获取Prometheus指标
|
||||
- ✅ 同步RapidAPI端点
|
||||
- ✅ APILLAMA处理API文档
|
||||
|
||||
### 2. MCP Server 基础服务 (3项测试)
|
||||
- ✅ 健康检查
|
||||
- ✅ 获取Prometheus指标
|
||||
- ✅ 获取工具列表
|
||||
|
||||
### 3. Agent 管理 (4项测试)
|
||||
- ✅ 获取Agent列表
|
||||
- ✅ 创建Agent
|
||||
- ✅ 获取特定Agent
|
||||
- ✅ 执行Agent工具
|
||||
|
||||
### 4. 认证模块 (4项测试)
|
||||
- ✅ 管理员登录
|
||||
- ✅ 渠道管理员登录
|
||||
- ✅ 供应商登录
|
||||
- ✅ 标准用户登录(预期失败,用户不存在)
|
||||
|
||||
### 5. 用户侧平台 API (7项测试)
|
||||
- ✅ 获取仪表板统计
|
||||
- ✅ 获取Agent活动数据
|
||||
- ✅ 选择网关类型
|
||||
- ✅ 获取网关API列表
|
||||
- ✅ 获取网关监控数据
|
||||
- ✅ 获取平台Agent列表
|
||||
- ✅ 获取余额信息
|
||||
|
||||
### 6. 渠道合作伙伴 API (3项测试)
|
||||
- ✅ 获取渠道仪表板统计
|
||||
- ✅ 获取租户列表
|
||||
- ✅ 获取渠道计费统计
|
||||
|
||||
### 7. 超级管理员 API (7项测试)
|
||||
- ✅ 获取平台统计
|
||||
- ✅ 获取渠道列表
|
||||
- ✅ 获取所有申请
|
||||
- ✅ 获取所有模型供应商
|
||||
- ✅ 获取所有Agent资源
|
||||
- ✅ 监控Agent健康状态
|
||||
- ✅ 获取三维度计费统计
|
||||
|
||||
### 8. 供应商管理 API (1项测试)
|
||||
- ✅ 获取模型供应商列表
|
||||
|
||||
### 9. MCP 监控 API (5项测试)
|
||||
- ✅ 获取系统性能指标
|
||||
- ✅ 获取服务统计信息
|
||||
- ✅ 获取性能趋势数据
|
||||
- ✅ 获取系统告警
|
||||
- ✅ 获取监控仪表盘聚合
|
||||
|
||||
---
|
||||
|
||||
## 🎯 测试结果详情
|
||||
|
||||
### 系统健康状态
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"services": {
|
||||
"data_ingestion": "healthy",
|
||||
"redis": "healthy",
|
||||
"nats": "healthy",
|
||||
"database": "healthy"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 系统性能指标
|
||||
```json
|
||||
{
|
||||
"system": {
|
||||
"cpu_usage_percent": 3.8,
|
||||
"memory_usage_percent": 30.8,
|
||||
"disk_usage_percent": 13.6
|
||||
},
|
||||
"services": {
|
||||
"active_agents": 3,
|
||||
"total_executions_24h": 2,
|
||||
"daily_active_users": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 认证测试
|
||||
- ✅ 管理员登录成功,获取token
|
||||
- ✅ 渠道管理员登录成功,获取token(包含channelId)
|
||||
- ✅ 供应商登录成功,获取token
|
||||
- ✅ 标准用户登录(预期401,用户不存在)
|
||||
|
||||
### Agent管理测试
|
||||
- ✅ 成功创建3个测试Agent
|
||||
- ✅ 成功执行Agent工具(math_add)
|
||||
- ✅ 计费记录正确创建
|
||||
- ✅ EU消费正确计算
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署状态
|
||||
|
||||
### Docker容器状态
|
||||
```
|
||||
NAME STATUS
|
||||
taiji-api-gateway Up (healthy)
|
||||
taiji-data-ingestion Up (healthy)
|
||||
taiji-grafana Up
|
||||
taiji-litellm-gateway Up (healthy)
|
||||
taiji-mcp-server Up (healthy)
|
||||
taiji-nats Up
|
||||
taiji-prometheus Up
|
||||
```
|
||||
|
||||
### 端口映射
|
||||
- Data Ingestion: `http://localhost:8001`
|
||||
- MCP Server: `http://localhost:8002`
|
||||
- LiteLLM Gateway: `http://localhost:4000`
|
||||
- API Gateway: `http://localhost:80`
|
||||
- Prometheus: `http://localhost:9090`
|
||||
- Grafana: `http://localhost:3000`
|
||||
- NATS: `nats://localhost:4222`
|
||||
|
||||
---
|
||||
|
||||
## 📝 测试脚本
|
||||
|
||||
完整的测试脚本位于: `scripts/test_all_apis.py`
|
||||
|
||||
### 运行测试
|
||||
```bash
|
||||
cd /home/taiji/tools/taiji-AI-PAD
|
||||
python3 scripts/test_all_apis.py
|
||||
```
|
||||
|
||||
### 测试特点
|
||||
- 自动化测试所有API端点
|
||||
- 支持认证token管理
|
||||
- 详细的错误报告
|
||||
- 彩色输出和进度显示
|
||||
- 100%测试覆盖率
|
||||
|
||||
---
|
||||
|
||||
## ✅ 结论
|
||||
|
||||
所有40项API测试全部通过,系统运行稳定,功能完整。主要修复了以下问题:
|
||||
|
||||
1. ✅ 数据库约束错误
|
||||
2. ✅ 认证token缺失字段
|
||||
3. ✅ 唯一约束冲突
|
||||
4. ✅ 响应格式处理
|
||||
|
||||
系统已经可以正常使用,所有核心功能均已验证。
|
||||
|
||||
---
|
||||
|
||||
**测试人员**: AI Assistant
|
||||
**审核人员**: 待定
|
||||
**下次测试日期**: 根据需要
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
# Taiji AI-PAD 普通用户创建和登录功能测试报告
|
||||
|
||||
## 测试时间
|
||||
2025-12-25
|
||||
|
||||
## 测试环境
|
||||
- 服务地址: http://localhost:8002
|
||||
- 服务状态: ✅ Healthy
|
||||
- 数据库状态: ✅ Healthy
|
||||
|
||||
## 测试结果总结
|
||||
|
||||
### ✅ 成功的功能 (7/9) - 通过率: 77.8%
|
||||
|
||||
1. **管理员创建** - ✅ 通过
|
||||
- 端点: `/api/admin/auth/login`
|
||||
- 功能: 自动创建管理员账号
|
||||
- 测试账号: admin_test@test.com
|
||||
|
||||
2. **普通用户创建** - ✅ 通过
|
||||
- 端点: `/api/channel/tenants/create`
|
||||
- 功能: 通过渠道管理员创建租户(普通用户)
|
||||
- 测试账号: normaluser@test.com
|
||||
- 关联渠道: test_channel@test.com
|
||||
|
||||
3. **普通用户登录** - ✅ 通过
|
||||
- 端点: `/api/auth/login`
|
||||
- 角色: user
|
||||
- JWT Token: ✅ 成功返回
|
||||
- 用户信息: ✅ 完整返回(包含channelId)
|
||||
|
||||
4. **用户仪表板访问** - ✅ 通过
|
||||
- 端点: `/api/user/dashboard/stats`
|
||||
- 返回数据: activeAgents, totalRequests, euBalance, systemHealth
|
||||
- 认证方式: Bearer Token ✅
|
||||
|
||||
5. **Agent列表查询** - ✅ 通过
|
||||
- 端点: `/agents`
|
||||
- 功能: 查询用户可用的Agent列表
|
||||
- 认证: ✅ 正常
|
||||
|
||||
6. **错误密码拒绝** - ✅ 通过
|
||||
- 状态码: 401 Unauthorized
|
||||
- 安全性: ✅ 正确拒绝错误密码
|
||||
|
||||
7. **用户登出** - ✅ 通过
|
||||
- 端点: `/api/auth/logout`
|
||||
- 功能: 用户登出功能正常
|
||||
|
||||
### ⚠️ 问题项 (2/9)
|
||||
|
||||
1. **渠道创建失败** - ⚠️
|
||||
- 端点: `/api/admin/channels/create`
|
||||
- 原因: 渠道已存在(邮箱重复)
|
||||
- 影响: 不影响测试,使用已存在的渠道
|
||||
- 状态: 非关键问题
|
||||
|
||||
2. **用户余额查询** - ❌ 404
|
||||
- 端点: `/api/user/balance`
|
||||
- 问题: 端点不存在或路径错误
|
||||
- 影响: 余额查询功能不可用
|
||||
- 建议: 检查API路由配置
|
||||
|
||||
## 测试流程
|
||||
|
||||
### 完整的普通用户创建和登录流程
|
||||
|
||||
```
|
||||
1. 管理员账号创建/登录
|
||||
↓
|
||||
2. 管理员创建渠道
|
||||
↓
|
||||
3. 渠道管理员登录(role=channel)
|
||||
↓
|
||||
4. 渠道管理员创建租户(普通用户)
|
||||
↓
|
||||
5. 普通用户登录(role=user)
|
||||
↓
|
||||
6. 普通用户访问受保护的API
|
||||
```
|
||||
|
||||
### 关键发现
|
||||
|
||||
#### 1. 渠道管理员登录方式
|
||||
- ❌ **不推荐**: `/api/channel/auth/login`
|
||||
- 问题: 创建的是普通用户,不是真正的渠道记录
|
||||
- 无channelId,无法创建租户
|
||||
|
||||
- ✅ **推荐**: `/api/auth/login` + `role="channel"`
|
||||
- 正确: 从Channel表查找,有channelId
|
||||
- 可以正常创建租户
|
||||
|
||||
#### 2. 用户数据结构
|
||||
```json
|
||||
{
|
||||
"id": "e2224c57-ed0a-4047-8e3d-204ac8432c19",
|
||||
"name": "普通用户",
|
||||
"email": "normaluser@test.com",
|
||||
"role": "user",
|
||||
"channelId": "fc9020ac-cdd7-4924-8941-afb2be33bc74"
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. JWT Token结构
|
||||
```
|
||||
Token包含信息:
|
||||
- sub: 用户ID
|
||||
- email: 用户邮箱
|
||||
- role: 用户角色(user)
|
||||
- channelId: 关联的渠道ID
|
||||
```
|
||||
|
||||
## 功能验证
|
||||
|
||||
### ✅ 已验证的功能
|
||||
|
||||
1. **账号创建**
|
||||
- ✅ 管理员可以创建渠道
|
||||
- ✅ 渠道管理员可以创建租户(普通用户)
|
||||
- ✅ 用户信息正确保存到数据库
|
||||
- ✅ 用户与渠道正确关联
|
||||
|
||||
2. **登录认证**
|
||||
- ✅ 普通用户使用邮箱密码登录
|
||||
- ✅ JWT Token正常生成和返回
|
||||
- ✅ 错误密码正确拒绝
|
||||
- ✅ 不存在用户正确拒绝
|
||||
|
||||
3. **权限控制**
|
||||
- ✅ Bearer Token认证机制正常
|
||||
- ✅ 用户可以访问自己的仪表板
|
||||
- ✅ 用户可以查询Agent列表
|
||||
- ✅ 角色权限验证正常
|
||||
|
||||
4. **会话管理**
|
||||
- ✅ 登出功能正常
|
||||
- ✅ Token有效期控制
|
||||
|
||||
## API端点清单
|
||||
|
||||
### 成功验证的端点
|
||||
|
||||
| 端点 | 方法 | 功能 | 状态 |
|
||||
|------|------|------|------|
|
||||
| `/api/admin/auth/login` | POST | 管理员登录 | ✅ |
|
||||
| `/api/auth/login` | POST | 统一登录(支持channel角色) | ✅ |
|
||||
| `/api/channel/tenants/create` | POST | 创建租户 | ✅ |
|
||||
| `/api/user/dashboard/stats` | GET | 用户仪表板 | ✅ |
|
||||
| `/agents` | GET | Agent列表 | ✅ |
|
||||
| `/api/auth/logout` | POST | 用户登出 | ✅ |
|
||||
|
||||
### 需要修复的端点
|
||||
|
||||
| 端点 | 方法 | 问题 | 优先级 |
|
||||
|------|------|------|--------|
|
||||
| `/api/user/balance` | GET | 404 Not Found | 中 |
|
||||
| `/api/admin/channels/create` | POST | 重复创建报错 | 低 |
|
||||
|
||||
## 测试账号
|
||||
|
||||
### 创建的测试账号
|
||||
|
||||
1. **管理员**
|
||||
- 邮箱: admin_test@test.com
|
||||
- 密码: admin123
|
||||
- 角色: admin
|
||||
|
||||
2. **渠道**
|
||||
- 名称: 测试渠道
|
||||
- 邮箱: test_channel@test.com
|
||||
- 密码: channel123
|
||||
- 佣金率: 10%
|
||||
|
||||
3. **普通用户(租户)**
|
||||
- 姓名: 普通用户
|
||||
- 邮箱: normaluser@test.com
|
||||
- 密码: user123456
|
||||
- 角色: user
|
||||
- 订阅等级: free
|
||||
- 关联渠道: test_channel@test.com
|
||||
|
||||
## 安全特性验证
|
||||
|
||||
- ✅ 密码bcrypt加密存储
|
||||
- ✅ JWT Token认证
|
||||
- ✅ 错误密码拒绝(401)
|
||||
- ✅ 角色权限验证
|
||||
- ✅ Bearer Token访问控制
|
||||
|
||||
## 建议
|
||||
|
||||
### 1. API路由修复
|
||||
- 检查并修复 `/api/user/balance` 端点
|
||||
- 确保所有用户相关API路由正确配置
|
||||
|
||||
### 2. 错误处理改进
|
||||
- 渠道重复创建应返回更明确的错误信息
|
||||
- 建议返回409 Conflict而不是400
|
||||
|
||||
### 3. 文档更新
|
||||
- 更新API文档,明确说明渠道登录的正确方式
|
||||
- 添加普通用户创建流程的完整示例
|
||||
|
||||
### 4. 测试覆盖
|
||||
- 添加用户充值功能测试
|
||||
- 添加用户Agent执行功能测试
|
||||
- 添加用户账单查询功能测试
|
||||
|
||||
## 结论
|
||||
|
||||
✅ **普通用户创建和登录核心功能正常运行!**
|
||||
|
||||
### 关键成果
|
||||
- ✅ 普通用户创建流程完整可用
|
||||
- ✅ 登录认证机制工作正常
|
||||
- ✅ JWT Token认证成功
|
||||
- ✅ 用户权限控制有效
|
||||
- ✅ 基础API访问正常
|
||||
|
||||
### 通过率: **77.8% (7/9)**
|
||||
|
||||
系统已具备完整的用户注册、登录和基础功能访问能力,可以支持普通用户的正常使用。剩余问题为非关键功能,不影响核心业务流程。
|
||||
|
||||
## 测试脚本
|
||||
- 位置: `/home/taiji/tools/taiji-AI-PAD/scripts/test_normal_user_v2.py`
|
||||
- 运行命令: `python3 scripts/test_normal_user_v2.py`
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
# Taiji AI-PAD 账号注册和登录功能测试报告
|
||||
|
||||
## 测试时间
|
||||
2025-12-25
|
||||
|
||||
## 测试环境
|
||||
- 服务地址: http://localhost:8002
|
||||
- 服务状态: ✅ Healthy
|
||||
- 数据库状态: ✅ Healthy
|
||||
|
||||
## 测试结果总结
|
||||
|
||||
### ✅ 成功的功能 (8/11)
|
||||
|
||||
1. **健康检查** - ✅ 通过
|
||||
- 端点: `/health`
|
||||
- 状态: 正常
|
||||
|
||||
2. **管理员账号自动创建** - ✅ 通过
|
||||
- 端点: `/api/admin/auth/login`
|
||||
- 功能: 首次登录自动创建账号
|
||||
- 测试账号: admin@test.com
|
||||
|
||||
3. **渠道管理员账号自动创建** - ✅ 通过
|
||||
- 端点: `/api/channel/auth/login`
|
||||
- 功能: 首次登录自动创建账号
|
||||
- 测试账号: channel@test.com
|
||||
|
||||
4. **供应商账号自动创建** - ✅ 通过
|
||||
- 端点: `/api/providers/auth/login`
|
||||
- 功能: 首次登录自动创建账号
|
||||
- 测试账号: provider@test.com
|
||||
|
||||
5. **管理员API访问** - ✅ 通过
|
||||
- 端点: `/api/admin/dashboard/stats`
|
||||
- 功能: 使用JWT token访问受保护的API
|
||||
- 认证方式: Bearer Token
|
||||
|
||||
6. **渠道管理员API访问** - ✅ 通过
|
||||
- 端点: `/api/channel/dashboard/stats`
|
||||
- 功能: 渠道仪表板数据访问
|
||||
|
||||
7. **供应商API访问** - ✅ 通过
|
||||
- 端点: `/api/providers/models`
|
||||
- 功能: 供应商模型列表访问
|
||||
|
||||
8. **错误处理** - ✅ 通过
|
||||
- 错误密码正确拒绝 (401)
|
||||
- 不存在用户正确拒绝 (401)
|
||||
|
||||
### ⚠️ 需要注意的问题 (3/11)
|
||||
|
||||
1. **标准Auth端点登录** - ⚠️ 部分失败
|
||||
- 端点: `/api/auth/login`
|
||||
- 问题: 角色验证严格,frontend-integration创建的用户角色为"user",无法以admin/provider角色登录
|
||||
- 原因: 角色不匹配(用户role="user",但尝试以"admin"/"provider"登录)
|
||||
- 影响: 需要确保用户创建时设置正确的角色
|
||||
|
||||
2. **bcrypt密码哈希** - ⚠️ 已修复
|
||||
- 问题: bcrypt版本兼容性导致密码哈希失败
|
||||
- 解决方案: 更新bcrypt到4.0.1版本
|
||||
- 状态: ✅ 已解决
|
||||
|
||||
3. **数据库字段约束** - ⚠️ 已修复
|
||||
- 问题: User表的name字段NOT NULL约束
|
||||
- 解决方案: 在ensure_user函数中添加name字段
|
||||
- 状态: ✅ 已解决
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 自动账号创建
|
||||
- ✅ Frontend Integration端点支持首次登录自动创建账号
|
||||
- ✅ 自动生成用户名、设置默认值
|
||||
- ✅ 密码使用bcrypt加密存储
|
||||
|
||||
### 2. JWT Token认证
|
||||
- ✅ 登录成功返回JWT token
|
||||
- ✅ Token包含用户信息(sub, email, role)
|
||||
- ✅ Token有效期: 3600秒 (1小时)
|
||||
|
||||
### 3. 多角色支持
|
||||
- ✅ 管理员 (admin, super_admin)
|
||||
- ✅ 渠道管理员 (channel_admin)
|
||||
- ✅ 供应商 (provider_admin)
|
||||
- ✅ 普通用户 (user)
|
||||
- ✅ 计费管理员 (billing_admin)
|
||||
- ✅ 运营管理员 (operations_admin)
|
||||
|
||||
### 4. 安全特性
|
||||
- ✅ 密码bcrypt加密
|
||||
- ✅ 错误密码拒绝访问
|
||||
- ✅ 不存在用户拒绝访问
|
||||
- ✅ JWT token验证
|
||||
- ✅ 角色权限验证
|
||||
|
||||
## API端点清单
|
||||
|
||||
### Frontend Integration端点
|
||||
1. `/api/admin/auth/login` - 管理员登录(自动创建)
|
||||
2. `/api/channel/auth/login` - 渠道管理员登录(自动创建)
|
||||
3. `/api/providers/auth/login` - 供应商登录(自动创建)
|
||||
|
||||
### 标准Auth端点
|
||||
1. `/api/auth/login` - 统一登录端点(需要指定role)
|
||||
2. `/api/auth/logout` - 登出
|
||||
|
||||
### 受保护的API端点
|
||||
1. `/api/admin/dashboard/stats` - 管理员仪表板
|
||||
2. `/api/channel/dashboard/stats` - 渠道仪表板
|
||||
3. `/api/providers/models` - 供应商模型列表
|
||||
|
||||
## 建议
|
||||
|
||||
1. **角色管理改进**
|
||||
- 建议在frontend-integration端点创建用户时,根据端点类型设置正确的角色
|
||||
- admin端点创建的用户应设置role为"admin"或"super_admin"
|
||||
- provider端点创建的用户应设置role为"provider_admin"
|
||||
|
||||
2. **用户注册端点**
|
||||
- 考虑添加公开的用户注册端点,允许新用户自主注册
|
||||
|
||||
3. **密码策略**
|
||||
- 建议添加密码复杂度验证
|
||||
- 建议添加密码重置功能
|
||||
|
||||
## 结论
|
||||
|
||||
✅ **账号注册和登录核心功能正常运行**
|
||||
|
||||
- 自动账号创建功能完善
|
||||
- JWT认证机制工作正常
|
||||
- 多角色支持完整
|
||||
- 安全特性到位
|
||||
- API访问控制有效
|
||||
|
||||
通过率: **73% (8/11)**
|
||||
|
||||
主要问题已修复,剩余问题为角色匹配逻辑,不影响核心功能使用。
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
# 缺失的 API 接口清单
|
||||
|
||||
根据前端代码和 API 文档的对比,以下接口在 API 文档中**没有提供**:
|
||||
|
||||
## 1. 删除 Agent 接口
|
||||
|
||||
**前端使用位置**: `/app/admin/dashboard/page.tsx` (资源管理标签页)
|
||||
|
||||
**当前实现**:
|
||||
- 前端使用 `TaijiAPIClient.deleteTool(agent.id || agent.name)` 来删除 Agent
|
||||
- 这个接口实际上是删除 Data Ingestion 服务中的工具,而不是删除 Agent 资源
|
||||
|
||||
**需要的接口**:
|
||||
```
|
||||
DELETE /api/admin/resources/agents/{agent_id}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8002/api/admin/resources/agents/agent-uuid-1" \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Agent资源已删除"
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 当前前端代码错误地使用了 `deleteTool` 接口来删除 Agent
|
||||
- 应该提供一个专门的删除 Agent 资源的接口
|
||||
- 删除应该是软删除,仅标记为不活跃
|
||||
|
||||
---
|
||||
|
||||
## 2. 删除渠道接口
|
||||
|
||||
**前端使用位置**: `/app/admin/dashboard/page.tsx` (渠道管理标签页)
|
||||
|
||||
**当前状态**:
|
||||
- 前端UI中有"删除渠道"按钮,但没有实现对应的API调用
|
||||
|
||||
**需要的接口**:
|
||||
```
|
||||
DELETE /api/admin/channels/{channel_id}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8002/api/admin/channels/channel-uuid-1" \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "渠道已删除"
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 删除渠道前应该检查是否有关联的租户
|
||||
- 如果有租户,应该提示或阻止删除
|
||||
- 删除应该是软删除,仅标记为不活跃
|
||||
|
||||
---
|
||||
|
||||
## 3. 更新渠道信息接口
|
||||
|
||||
**前端使用位置**: `/app/admin/dashboard/page.tsx` (渠道管理标签页)
|
||||
|
||||
**当前状态**:
|
||||
- 前端可能有编辑渠道信息的功能,但需要确认是否有对应的API
|
||||
|
||||
**需要的接口**:
|
||||
```
|
||||
PUT /api/admin/channels/{channel_id}
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "合作渠道A(更新)",
|
||||
"email": "new-email@channel-a.com",
|
||||
"commissionRate": 12.0,
|
||||
"status": "active"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "channel-uuid-1",
|
||||
"name": "合作渠道A(更新)",
|
||||
"email": "new-email@channel-a.com"
|
||||
},
|
||||
"message": "渠道信息更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 更新 Agent 资源配置接口
|
||||
|
||||
**前端使用位置**: `/app/admin/dashboard/page.tsx` (资源管理标签页 - Agent资源配置对话框)
|
||||
|
||||
**当前状态**:
|
||||
- 前端有配置 Agent CPU 和内存的对话框,但保存时没有调用API
|
||||
|
||||
**需要的接口**:
|
||||
```
|
||||
PUT /api/admin/resources/agents/{agent_id}/config
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"cpu": 4.0,
|
||||
"memory": 8.0,
|
||||
"maxInstances": 10
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Agent资源配置更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
### 已实现的接口(2025-12-25 更新):
|
||||
1. ✅ **DELETE /api/admin/resources/agents/{agent_id}** - 删除Agent资源(软删除)
|
||||
2. ✅ **DELETE /api/admin/channels/{channel_id}** - 删除渠道(软删除,检查关联租户)
|
||||
3. ✅ **PUT /api/admin/channels/{channel_id}** - 更新渠道信息
|
||||
4. ✅ **PUT /api/admin/resources/agents/{agent_id}/config** - 更新Agent资源配置
|
||||
|
||||
### 所有接口状态:
|
||||
✅ 所有接口都已实现并在API文档中有说明
|
||||
|
||||
---
|
||||
|
||||
## 实现说明
|
||||
|
||||
### 2025-12-25 新增接口
|
||||
|
||||
1. **DELETE /api/admin/resources/agents/{agent_id}**
|
||||
- 软删除Agent资源,将状态标记为 `inactive`
|
||||
- 需要超级管理员权限
|
||||
|
||||
2. **DELETE /api/admin/channels/{channel_id}**
|
||||
- 软删除渠道,将状态标记为 `inactive`
|
||||
- 删除前检查是否有活跃租户,如有则拒绝删除
|
||||
- 需要超级管理员权限
|
||||
|
||||
3. **PUT /api/admin/channels/{channel_id}**
|
||||
- 更新渠道的名称、邮箱、佣金比例、状态
|
||||
- 更新邮箱时检查是否与其他渠道冲突
|
||||
- 需要超级管理员权限
|
||||
|
||||
4. **PUT /api/admin/resources/agents/{agent_id}/config**
|
||||
- 更新Agent的CPU、内存、最大实例数配置
|
||||
- 需要超级管理员权限
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成初始用户的SQL插入语句
|
||||
使用bcrypt加密密码
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
import bcrypt
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""使用bcrypt加密密码"""
|
||||
# bcrypt需要bytes类型的密码
|
||||
password_bytes = password.encode('utf-8')
|
||||
# 生成salt并加密
|
||||
salt = bcrypt.gensalt()
|
||||
hashed = bcrypt.hashpw(password_bytes, salt)
|
||||
# 返回字符串形式
|
||||
return hashed.decode('utf-8')
|
||||
|
||||
# 定义初始用户
|
||||
USERS = [
|
||||
{
|
||||
"name": "超级管理员",
|
||||
"username": "admin",
|
||||
"email": "admin@test.com",
|
||||
"password": "admin123",
|
||||
"role": "super_admin",
|
||||
"full_name": "系统超级管理员",
|
||||
"is_active": True,
|
||||
"is_admin": True,
|
||||
"subscription_tier": "enterprise",
|
||||
"balance": 100000,
|
||||
"credit_limit": 500000,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "渠道管理员",
|
||||
"username": "channel",
|
||||
"email": "channel@test.com",
|
||||
"password": "channel123",
|
||||
"role": "channel_admin",
|
||||
"full_name": "渠道管理员",
|
||||
"is_active": True,
|
||||
"is_admin": False,
|
||||
"subscription_tier": "business",
|
||||
"balance": 50000,
|
||||
"credit_limit": 100000,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "供应商管理员",
|
||||
"username": "provider",
|
||||
"email": "provider@test.com",
|
||||
"password": "provider123",
|
||||
"role": "provider_admin",
|
||||
"full_name": "供应商管理员",
|
||||
"is_active": True,
|
||||
"is_admin": False,
|
||||
"subscription_tier": "business",
|
||||
"balance": 50000,
|
||||
"credit_limit": 100000,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "测试用户",
|
||||
"username": "user",
|
||||
"email": "user@test.com",
|
||||
"password": "user123",
|
||||
"role": "user",
|
||||
"full_name": "普通测试用户",
|
||||
"is_active": True,
|
||||
"is_admin": False,
|
||||
"subscription_tier": "free",
|
||||
"balance": 100,
|
||||
"credit_limit": 1000,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "计费管理员",
|
||||
"username": "billing",
|
||||
"email": "billing@test.com",
|
||||
"password": "billing123",
|
||||
"role": "billing_admin",
|
||||
"full_name": "计费管理员",
|
||||
"is_active": True,
|
||||
"is_admin": False,
|
||||
"subscription_tier": "business",
|
||||
"balance": 10000,
|
||||
"credit_limit": 50000,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "运维管理员",
|
||||
"username": "operations",
|
||||
"email": "operations@test.com",
|
||||
"password": "operations123",
|
||||
"role": "operations_admin",
|
||||
"full_name": "运维管理员",
|
||||
"is_active": True,
|
||||
"is_admin": False,
|
||||
"subscription_tier": "business",
|
||||
"balance": 10000,
|
||||
"credit_limit": 50000,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def generate_sql():
|
||||
"""生成SQL插入语句"""
|
||||
print("-- 初始用户数据")
|
||||
print("-- 自动生成时间:", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print("-- 注意: 此脚本会在用户不存在时插入初始用户\n")
|
||||
|
||||
for user in USERS:
|
||||
user_id = str(uuid.uuid4())
|
||||
password_hash = hash_password(user["password"])
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
print(f"-- 创建用户: {user['name']} ({user['email']})")
|
||||
print(f"INSERT INTO users (")
|
||||
print(f" id, created_at, updated_at,")
|
||||
print(f" name, username, email,")
|
||||
print(f" password_hash, hashed_password,")
|
||||
print(f" role, full_name,")
|
||||
print(f" is_active, is_admin,")
|
||||
print(f" subscription_tier, balance, credit_limit,")
|
||||
print(f" status, discount")
|
||||
print(f") VALUES (")
|
||||
print(f" '{user_id}', '{now}', '{now}',")
|
||||
print(f" '{user['name']}', '{user['username']}', '{user['email']}',")
|
||||
print(f" '{password_hash}', '{password_hash}',")
|
||||
print(f" '{user['role']}', '{user['full_name']}',")
|
||||
print(f" {user['is_active']}, {user['is_admin']},")
|
||||
print(f" '{user['subscription_tier']}', {user['balance']}, {user['credit_limit']},")
|
||||
print(f" '{user['status']}', 0")
|
||||
print(f") ON CONFLICT (email) DO NOTHING;")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
print("="*80)
|
||||
print("Taiji AI-PAD 初始用户SQL生成器")
|
||||
print("="*80)
|
||||
print()
|
||||
|
||||
generate_sql()
|
||||
|
||||
print("\n-- 用户信息汇总:")
|
||||
print("-- " + "="*76)
|
||||
for user in USERS:
|
||||
print(f"-- {user['name']:15} | {user['email']:25} | 密码: {user['password']}")
|
||||
print("-- " + "="*76)
|
||||
print("\n-- 提示: 将上述SQL语句添加到 scripts/init.sql 文件末尾")
|
||||
print("-- 或者创建新的 scripts/init_users.sql 文件")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -18,6 +18,8 @@ from app.auth import require_auth, get_password_hash
|
||||
from app.schemas import (
|
||||
SuccessResponse,
|
||||
CreateChannelRequest,
|
||||
UpdateChannelRequest,
|
||||
UpdateAgentConfigRequest,
|
||||
ChannelInfo,
|
||||
ChannelResourceAllocation,
|
||||
ApplicationInfo,
|
||||
@@ -164,6 +166,121 @@ async def create_channel(
|
||||
)
|
||||
|
||||
|
||||
@router.put("/channels/{channel_id}", response_model=SuccessResponse)
|
||||
async def update_channel(
|
||||
channel_id: str,
|
||||
req: UpdateChannelRequest,
|
||||
principal: dict = Depends(require_auth),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
更新渠道信息
|
||||
"""
|
||||
_verify_admin_permission(principal)
|
||||
|
||||
# 验证渠道存在
|
||||
result = await db.execute(
|
||||
select(Channel).where(Channel.id == channel_id)
|
||||
)
|
||||
channel = result.scalar_one_or_none()
|
||||
|
||||
if not channel:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="渠道不存在"
|
||||
)
|
||||
|
||||
# 如果更新邮箱,检查邮箱是否已被其他渠道使用
|
||||
if req.email and req.email != channel.email:
|
||||
email_check = await db.execute(
|
||||
select(Channel).where(
|
||||
and_(
|
||||
Channel.email == req.email,
|
||||
Channel.id != channel_id
|
||||
)
|
||||
)
|
||||
)
|
||||
if email_check.scalar_one_or_none():
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="邮箱已被其他渠道使用"
|
||||
)
|
||||
channel.email = req.email
|
||||
|
||||
# 更新其他字段
|
||||
if req.name is not None:
|
||||
channel.name = req.name
|
||||
if req.commissionRate is not None:
|
||||
channel.commission_rate = req.commissionRate
|
||||
if req.status is not None:
|
||||
channel.status = req.status
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(channel)
|
||||
|
||||
return SuccessResponse(
|
||||
data={
|
||||
"id": str(channel.id),
|
||||
"name": channel.name,
|
||||
"email": channel.email,
|
||||
"commissionRate": float(channel.commission_rate),
|
||||
"status": channel.status,
|
||||
},
|
||||
message="渠道信息更新成功"
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/channels/{channel_id}", response_model=SuccessResponse)
|
||||
async def delete_channel(
|
||||
channel_id: str,
|
||||
principal: dict = Depends(require_auth),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
删除渠道(软删除,标记为不活跃)
|
||||
"""
|
||||
_verify_admin_permission(principal)
|
||||
|
||||
# 验证渠道存在
|
||||
result = await db.execute(
|
||||
select(Channel).where(Channel.id == channel_id)
|
||||
)
|
||||
channel = result.scalar_one_or_none()
|
||||
|
||||
if not channel:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="渠道不存在"
|
||||
)
|
||||
|
||||
# 检查是否有关联的租户
|
||||
tenants_result = await db.execute(
|
||||
select(func.count(User.id)).where(
|
||||
and_(
|
||||
User.channel_id == channel_id,
|
||||
User.role == "user",
|
||||
User.status == "active"
|
||||
)
|
||||
)
|
||||
)
|
||||
tenant_count = tenants_result.scalar() or 0
|
||||
|
||||
if tenant_count > 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"渠道下有 {tenant_count} 个活跃租户,无法删除。请先移除或停用所有租户。"
|
||||
)
|
||||
|
||||
# 软删除:标记为不活跃
|
||||
channel.status = "inactive"
|
||||
await db.commit()
|
||||
|
||||
return SuccessResponse(
|
||||
data={"id": str(channel.id)},
|
||||
message="渠道已删除"
|
||||
)
|
||||
|
||||
|
||||
@router.put("/channels/{channel_id}/resources", response_model=SuccessResponse)
|
||||
async def allocate_channel_resources(
|
||||
channel_id: str,
|
||||
@@ -421,6 +538,86 @@ async def list_all_agents(
|
||||
return SuccessResponse(data={"agents": data})
|
||||
|
||||
|
||||
@router.delete("/resources/agents/{agent_id}", response_model=SuccessResponse)
|
||||
async def delete_agent_resource(
|
||||
agent_id: str,
|
||||
principal: dict = Depends(require_auth),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
删除Agent资源(软删除,标记为不活跃)
|
||||
"""
|
||||
_verify_admin_permission(principal)
|
||||
|
||||
# 验证Agent存在
|
||||
result = await db.execute(
|
||||
select(Agent).where(Agent.id == agent_id)
|
||||
)
|
||||
agent = result.scalar_one_or_none()
|
||||
|
||||
if not agent:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Agent资源不存在"
|
||||
)
|
||||
|
||||
# 软删除:标记为不活跃
|
||||
agent.status = "inactive"
|
||||
await db.commit()
|
||||
|
||||
return SuccessResponse(
|
||||
data={"id": str(agent.id), "name": agent.name},
|
||||
message="Agent资源已删除"
|
||||
)
|
||||
|
||||
|
||||
@router.put("/resources/agents/{agent_id}/config", response_model=SuccessResponse)
|
||||
async def update_agent_config(
|
||||
agent_id: str,
|
||||
req: UpdateAgentConfigRequest,
|
||||
principal: dict = Depends(require_auth),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
更新Agent资源配置(CPU、内存、最大实例数)
|
||||
"""
|
||||
_verify_admin_permission(principal)
|
||||
|
||||
# 验证Agent存在
|
||||
result = await db.execute(
|
||||
select(Agent).where(Agent.id == agent_id)
|
||||
)
|
||||
agent = result.scalar_one_or_none()
|
||||
|
||||
if not agent:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Agent资源不存在"
|
||||
)
|
||||
|
||||
# 更新配置
|
||||
if req.cpu is not None:
|
||||
agent.cpu = req.cpu
|
||||
if req.memory is not None:
|
||||
agent.memory = req.memory
|
||||
if req.maxInstances is not None:
|
||||
agent.max_instances = req.maxInstances
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(agent)
|
||||
|
||||
return SuccessResponse(
|
||||
data={
|
||||
"id": str(agent.id),
|
||||
"name": agent.name,
|
||||
"cpu": float(agent.cpu),
|
||||
"memory": float(agent.memory),
|
||||
"maxInstances": agent.max_instances,
|
||||
},
|
||||
message="Agent资源配置更新成功"
|
||||
)
|
||||
|
||||
|
||||
# ============= 监控 =============
|
||||
|
||||
@router.get("/monitoring/agents", response_model=SuccessResponse)
|
||||
|
||||
@@ -462,6 +462,21 @@ class CreateChannelRequest(BaseModel):
|
||||
commissionRate: float = Field(0, ge=0, le=100)
|
||||
|
||||
|
||||
class UpdateChannelRequest(BaseModel):
|
||||
"""更新渠道信息请求"""
|
||||
name: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
commissionRate: Optional[float] = Field(None, ge=0, le=100)
|
||||
status: Optional[str] = Field(None, pattern="^(active|inactive)$")
|
||||
|
||||
|
||||
class UpdateAgentConfigRequest(BaseModel):
|
||||
"""更新Agent资源配置请求"""
|
||||
cpu: Optional[float] = Field(None, ge=0.1, le=64)
|
||||
memory: Optional[float] = Field(None, ge=0.5, le=256)
|
||||
maxInstances: Optional[int] = Field(None, ge=1, le=1000)
|
||||
|
||||
|
||||
class ChannelInfo(BaseModel):
|
||||
"""渠道信息"""
|
||||
id: str
|
||||
|
||||
Reference in New Issue
Block a user