Files
cjyyzx/AI-BRAIN-API.md
xiaohei 428644e286
Revalidate Docs / Revalidate Docs (push) Failing after 2s
E2E CI / Check Duplicate Run (push) Failing after 5s
Test CI / Check Duplicate Run (push) Failing after 6s
E2E CI / Test Web App (push) Has been skipped
Test CI / Test Packages (push) Has been skipped
Test CI / Test App (shard 1/3) (push) Has been skipped
Test CI / Test App (shard 2/3) (push) Has been skipped
Test CI / Test App (shard 3/3) (push) Has been skipped
Test CI / Test Desktop App (push) Has been skipped
🔄 Branch Synchronization / sync-branches (push) Failing after 11s
Test CI / Test Database (push) Has been skipped
Test CI / Merge and Upload App Coverage (push) Has been skipped
Database Schema Visualization CI / build (push) Failing after 4m14s
Enterprise AI Workspace prototype: LobeChat (de-branded) + Enterprise Gateway + Postgres/Redis stack
2026-04-21 12:58:00 +08:00

1146 lines
43 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.
# AI 大脑 · 对内程序调用接口说明
本文档供 **内部 AI 大脑 / 自动化程序** 在回答 **费用、用量、账单、资源归属** 等问题时调用 CloudCost 后端使用。
- **范围**:以 **只读(GET)** 为主。
- **响应 JSON 约定**:
- `date` → `YYYY-MM-DD` 字符串
- `datetime` → 本地时间 ISO8601(**无时区后缀**,例:`2026-04-18T18:01:19.142386`)
- 金额 / 用量(**重要**):
- 聚合型 / 仪表盘类接口(`/api/dashboard/*`、`/api/service-accounts/{id}/costs`、`/api/service-accounts/daily-report`、`/api/dashboard/overview`)→ **JSON number**(float)
- 原始明细类接口(`/api/metering/*`、`/api/billing/detail`)→ **JSON string**(后端保留 Decimal 精度),需 `float(...)` / `parseFloat(...)` 解析
- **完整路由清单**:见 [API.md](./API.md)。
---
## 1. 认证体系概述
云管后端已接入 **Casdoor 统一认证**,所有请求(除匿名白名单外)必须携带合法凭据。
### 1.1 三种认证方式
| 方式 | Header | 适用场景 | 角色来源 |
|------|--------|----------|----------|
| Casdoor OAuth Cookie | 浏览器自动携带 `cc_access_token` | 人类用户通过前端登录 | Casdoor token 中的 roles |
| Casdoor Bearer Token | `Authorization: Bearer <token>` | 内部系统间调用(client_credentials) | token roles 非空时用 token;为空时 fallback 到 DB `users.roles` |
| API Key | `X-API-Key: cck_xxx` | 三方对接 / 细粒度权限控制 | DB `users.roles`(owner 用户) |
**AI 大脑 / 内部系统推荐使用方式 2(Casdoor Bearer Token)**,因为所有内部系统已在 Casdoor 注册了应用,统一走 `client_credentials` 拿 token 即可。
### 1.2 四个角色
| 角色 | 权限范围 |
|------|----------|
| `cloud_admin` | 全部功能 + 全量数据(超级角色,自动满足任何角色要求) |
| `cloud_ops` | dashboard + 触发同步 |
| `cloud_finance` | dashboard + 账单管理 |
| `cloud_viewer` | dashboard 只读 |
角色 → 前端页面映射
┌────────────────────────────────────────────┬─────────────┬───────────────┬───────────────┬───────────────┐
│ 前端页面 │ cloud_admin │ cloud_ops │ cloud_finance │ cloud_viewer │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 仪表盘 / (Dashboard) │ ✅ │ ✅ │ ✅ │ ✅ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 统计 /daily-report (日报/账单明细) │ ✅ │ ✅ │ ✅ │ ✅ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 计量 /metering │ ✅ │ ✅ │ ✅ │ ✅ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 告警 /alerts │ ✅ │ ✅ │ ✅ │ ✅ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 货源管理 /accounts (云账号管理) │ ✅ 可增删改 │ ❌ 只能看列表 │ ❌ 只能看列表 │ ❌ 只能看列表 │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 供应商管理 /suppliers │ ✅ 可增删改 │ ❌ │ ❌ │ ❌ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 模型管理 /azure-deploy │ ✅ │ ❌ │ ❌ │ ❌ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ Azure 接入 /azure-onboard │ ✅ │ ❌ │ ❌ │ ❌ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ 项目详情 /projects/[id] │ ✅ 可管理 │ ✅ 只读 │ ✅ 只读 │ ✅ 只读 │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ Header 中的 同步按钮 │ ✅ │ ✅ │ ❌ │ ❌ │
├────────────────────────────────────────────┼─────────────┼───────────────┼───────────────┼───────────────┤
│ Header 中的 账单管理(生成/调整/确认账单) │ ✅ │ ❌ │ ✅ │ ❌ │
└────────────────────────────────────────────┴─────────────┴───────────────┴───────────────┴───────────────┘
---
各角色详细说明
cloud_admin — 超级管理员
能用所有页面的全部功能,额外还有:
- 用户管理、权限分配(/api/admin/users/*)
- 云账号增删改(/api/cloud-accounts/ POST/PUT/DELETE)
- 模块开关(/api/api-permissions/)
- API Key 管理
- Azure 部署和 OAuth 授权
- 数据范围:看到全量数据,不受 cloud_account_grant 限制
cloud_ops — 运维
- 仪表盘:✅ 全部看板数据
- 统计/日报:✅ 账单明细查看和导出
- 同步操作:✅ 触发数据同步、查看同步日志(/api/sync/*)
- 告警:✅ 查看
- 计量:✅ 查看
- ❌ 不能管理云账号/供应商/Azure 部署/账单
- 数据范围:仅限被授权的云账号(通过 UserCloudAccountGrant)
cloud_finance — 财务
- 仪表盘:✅ 全部看板数据
- 统计/日报:✅ 账单明细查看和导出
- 账单管理:✅ 生成、调整、确认、标记已付(/api/bills/*)
- 告警:✅ 查看
- 计量:✅ 查看
- ❌ 不能触发同步、不能管理云账号/供应商/Azure
- 数据范围:仅限被授权的云账号
cloud_viewer — 只读查看者
- 仪表盘:✅ 只读
- 统计/日报:✅ 只读 + 导出
- 告警:✅ 只读
- 计量:✅ 只读
- ❌ 其他所有管理操作都不可用
- 数据范围:仅限被授权的云账号
---
角色管理方式:
- **人类用户**:在 Casdoor 后台 → Roles → 给用户分配角色,登录时自动带入
- **机器应用**(client_credentials):Casdoor token 不携带角色,管理员通过 SQL 设置 DB 中的 `users.roles`
### 1.3 模块开关
每个业务路由绑定一个模块名(如 `dashboard`、`billing`)。管理员可通过 `PATCH /api/api-permissions/<module>` 全局关停某模块,关停后所有身份调用都会 `403`。AI 应优雅降级,不要把 `403` 当故障。
### 1.4 数据可见范围
同一个 URL,不同身份返回的**数值不同**:
- `cloud_admin`(无额外限制)→ 全量数据
- 非 admin → 仅看到 `user_cloud_account_grants` 表里被授权的云账号数据
- Dashboard 聚合接口在 **SUM 之前**加 WHERE 过滤,百分比/增长率基于过滤后数据重算
- 如果没有可见数据,返回空数组或零值(不是 403)
AI 回答时应说明"当前视角内"的数据,避免把有限视角误报为全量。
---
## 2. AI 大脑接入指南
### 2.1 获取 Token(Casdoor client_credentials)
```bash
CASDOOR=https://casdoor.ashyglacier-8207efd2.eastasia.azurecontainerapps.io
TOKEN=$(curl -s -X POST "$CASDOOR/api/login/oauth/access_token" \
-d "grant_type=client_credentials&client_id=<你的APP_CLIENT_ID>&client_secret=<你的APP_CLIENT_SECRET>" \
| python -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
```
- token 默认有效期 **24 小时**,调用方应缓存并在过期前刷新
- 首次使用该 token 调用云管 API 时,后端会自动在 `users` 表创建一条记录(`casdoor_sub=admin/<app_name>`,`roles=[]`)
- **管理员需提前设置角色**(否则所有需要角色的接口返回 403):
```sql
UPDATE users SET roles='["cloud_admin"]'::jsonb WHERE casdoor_sub='admin/<app_name>';
```
### 2.2 调用接口
```bash
BASE=https://cloudcost-brank.yellowground-bf760827.southeastasia.azurecontainerapps.io
# 确认身份与可见范围
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/auth/me"
# 当月首页 bundle
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/dashboard/bundle?month=2026-04"
# 明细分页
curl -s -H "Authorization: Bearer $TOKEN" \
"$BASE/api/metering/detail?date_start=2026-04-01&date_end=2026-04-30&provider=aws&page=1&page_size=100"
```
### 2.3 `/api/auth/me` 响应说明
```json
{
"id": 4,
"username": "sales",
"email": "",
"display_name": "Sales App (M2M)",
"roles": ["cloud_admin"],
"visible_cloud_account_ids": null
}
```
`visible_cloud_account_ids`:
- `null` → 全量可见(`cloud_admin` 身份)
- `[]` → 零可见(未被授权任何云账号)
- `[1, 2]` → 只能看到这些云账号的数据
### 2.4 错误码
| HTTP 状态码 | 含义 | AI 应对 |
|---|---|---|
| `200` | 成功 | 正常解析 |
| `401` | 未带凭据或 token 过期 | 刷新 token 后重试 |
| `403 missing required role` | 角色不足 | 该接口对当前身份不可用,跳过 |
| `403 Module 'x' is disabled` | 模块被管理员关停 | 优雅降级,不报故障 |
| `422` | 参数校验失败 | 检查 Query 参数格式 |
| `503` | 数据库不可达 | 稍后重试 |
---
## 3. 接口分级
| 级别 | 含义 |
|------|------|
| **P0 推荐** | 费用总览、趋势、计量聚合、账单明细分页、数据新鲜度 |
| **P1 补充** | 维度拆分(分类/区域/项目排行)、服务账号上下文、月度账单、告警阈值执行态 |
| **P2 可选** | 资源清单、汇率、分类字典、导出类流式接口(适合落盘,不适合直接塞进模型上下文) |
| **禁止** | 任何 **写操作**、**凭据解密**、**同步触发**、**删除**、**Azure 部署**、以及返回 **webhook/邮箱** 等敏感配置的接口 |
---
## 4. P0 推荐接口(入参 / 出参)
### 4.1 `GET /api/health`
**用途**:连通性探测(匿名可访问)。
**出参**:
```json
{ "status": "ok" }
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `status` | string | 总是 `"ok"`;仅此一字段。失败时会被 FastAPI 500 覆盖,不返回此对象 |
---
### 4.2 `GET /api/sync/last`
**用途**:回答「数据同步到什么时候」。模块:`sync`,角色:`cloud_ops`(含 admin)。
**出参**(真实样例):
```json
{ "last_sync": "2026-04-18T18:01:19.142386" }
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `last_sync` | string \| null | 最近一次 **成功** 同步结束时间 ISO8601(本地时间,无时区)。从未同步过时为 `null` |
---
### 4.3 `GET /api/dashboard/bundle`
**用途**:**单次请求**拿首页级总览。模块:`dashboard`,角色:`cloud_viewer` / `cloud_ops` / `cloud_finance`(任一即可,`cloud_admin` 自动通过)。
**入参(Query)**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `month` | string | 是 | `YYYY-MM`,统计月 |
| `granularity` | string | 否 | `daily` \| `weekly` \| `monthly`,默认 `daily` |
| `service_limit` | int | 否 | 1–100,默认 10 |
**出参**(真实样例,截断):
```json
{
"overview": {
"total_cost": 149785.098288,
"prev_month_cost": 938396.218412,
"mom_change_pct": -84.04,
"active_projects": 7
},
"trend": [
{ "date": "2026-04-01", "cost": 7399.860517,
"cost_by_provider": { "aws": 466.038035, "azure": 962.156064, "gcp": 5971.666418 } }
],
"by_provider": [
{ "provider": "gcp", "cost": 124735.119318, "percentage": 83.28 },
{ "provider": "azure", "cost": 18058.538325, "percentage": 12.06 }
],
"by_service": [
{ "product": "Vertex AI", "cost": 124410.727567, "percentage": 83.06 },
{ "product": "Virtual Machines", "cost": 6202.288351, "percentage": 4.14 }
]
}
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `overview.total_cost` | number | 当月总费用(USD,对当前身份可见范围求和) |
| `overview.prev_month_cost` | number | 上月同口径总费用 |
| `overview.mom_change_pct` | number | 环比变化 %,保留两位小数,可能负值 |
| `overview.active_projects` | integer | `status=active` 的服务账号数 |
| `trend[].date` | string | `YYYY-MM-DD` |
| `trend[].cost` | number | 当日全 provider 合计费用 |
| `trend[].cost_by_provider` | object | 形如 `{ "aws":…, "gcp":…, "azure":… }`;只包含当日有数据的 provider |
| `by_provider[]` | array | 按 provider 当月累计,`cost` + `percentage`(占 total_cost 百分比,保留两位小数)|
| `by_service[]` | array | Top N 服务(`product` 原样字符串),N 由 `service_limit` 控制 |
---
### 4.4 `GET /api/dashboard/overview`
**用途**:只要月度总览卡片数据。
**入参(Query)**:`month`(必填,`YYYY-MM`)。
**出参**(真实样例):
```json
{
"total_cost": 149785.098288,
"prev_month_cost": 938396.218412,
"mom_change_pct": -84.04,
"active_projects": 7
}
```
字段同 §4.3 `overview`。
---
### 4.5 `GET /api/metering/summary`
**用途**:按条件汇总用量/费用。模块:`metering`,角色:任意登录即可。
**入参(Query)**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `date_start` | string | 否 | `YYYY-MM-DD` |
| `date_end` | string | 否 | `YYYY-MM-DD` |
| `provider` | string | 否 | `aws` / `gcp` / `azure` |
| `product` | string | 否 | 产品/服务名 |
| `account_id` | int | 否 | 服务账号 ID |
| `supply_source_id` | int | 否 | 货源 ID |
| `supplier_name` | string | 否 | 供应商名称 |
| `data_source_id` | int | 否 | 数据源 ID |
**出参**(真实样例,注意 `total_cost` / `total_usage` 是**字符串**):
```json
{
"total_cost": "149785.098288",
"total_usage": "55555350118.525461",
"record_count": 62016,
"service_count": 43
}
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `total_cost` | **string**(Decimal)| 筛选范围内的费用合计,USD。调用方自行 `float(...)` |
| `total_usage` | **string**(Decimal)| 用量合计。注意不同 `product` 的 `usage_unit` 不同,直接求和意义有限 |
| `record_count` | integer | 命中的 billing_data 行数 |
| `service_count` | integer | 不同 `product` 去重数 |
---
### 4.6 `GET /api/metering/daily`
**用途**:按日聚合费用与用量。入参同 `metering/summary`。
**出参**(真实样例):
```json
[
{ "date": "2026-04-01", "usage_quantity": "2327753993.353517",
"cost": "7399.860517", "record_count": 2745 },
{ "date": "2026-04-02", "usage_quantity": "1300687882.204744",
"cost": "6175.577245", "record_count": 2974 }
]
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `date` | string | `YYYY-MM-DD` |
| `cost` | **string**(Decimal)| 当日费用合计 |
| `usage_quantity` | **string**(Decimal)| 当日用量合计(跨 product;单位不定)|
| `record_count` | integer | 当日命中的明细行数 |
---
### 4.7 `GET /api/metering/by-service`
**用途**:按服务聚合(Top N 分析)。入参同 summary(无 `product` 过滤)。
**出参**(真实样例):
```json
[
{ "product": "Vertex AI", "usage_quantity": "55518458336.183727",
"usage_unit": "month", "cost": "124410.727567", "record_count": 58924 },
{ "product": "Cloud Text-to-Speech API", "usage_quantity": "18315835.000000",
"usage_unit": "count", "cost": "315.170942", "record_count": 101 }
]
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `product` | string | 原样服务名(含空格大小写;同名但不同 `usage_unit` 会分别出现,后端按 `(product, unit)` 分组再在此合并,见 `usage_unit`)|
| `cost` | **string**(Decimal)| 该服务筛选范围内的费用合计 |
| `usage_quantity` | **string**(Decimal)| 用量合计;仅在**该服务使用单一 `usage_unit`** 时可直接阅读 |
| `usage_unit` | string \| null | 该服务的用量单位;如果同名 product 有多种单位,后端取任一 |
| `record_count` | integer | 行数 |
---
### 4.8 `GET /api/metering/detail`
**用途**:原始明细行分页。入参在 summary 基础上增加 `page`(默认 1)、`page_size`(默认 50,最大 500)。
**出参**(真实样例):
```json
[
{
"id": 1819145,
"date": "2026-04-18",
"provider": "gcp",
"data_source_id": 3,
"project_id": "ysgemini-20260324",
"product": "Cloud Text-to-Speech API",
"usage_type": "Cloud TTS API text input token count for Gemini 2.5 Pro",
"region": "asia-southeast1",
"cost": "0.031815",
"usage_quantity": "31815.000000",
"usage_unit": "count",
"currency": "USD"
}
]
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | integer | 明细主键(`billing_data.id`)|
| `date` | string | `YYYY-MM-DD` 账单日期 |
| `provider` | string | `aws` / `gcp` / `azure` |
| `data_source_id` | integer | 数据源 FK |
| `project_id` | string | **云厂商侧项目 ID**(GCP project / Azure subscription / AWS account),不是本地 `projects.id` |
| `product` | string \| null | 服务名 |
| `usage_type` | string \| null | 计费类型 / SKU 名 |
| `region` | string \| null | 区域 |
| `cost` | **string**(Decimal)| 该条费用 USD |
| `usage_quantity` | **string**(Decimal)| 用量 |
| `usage_unit` | string \| null | 用量单位 |
| `currency` | string | 总是 `"USD"` |
---
### 4.9 `GET /api/metering/detail/count`
**用途**:与 `detail` 同筛选条件下的总条数。
**出参**:`{ "total": 62016 }`
| 字段 | 类型 | 说明 |
|---|---|---|
| `total` | integer | 行数;分页计算用 |
---
### 4.10 `GET /api/billing/detail`
**用途**:计费明细列表。模块:`billing`,角色:任意登录。数据按可见数据源过滤。
**入参(Query)**:
| 参数 | 类型 | 说明 |
|------|------|------|
| `date_start` | string | `YYYY-MM-DD` |
| `date_end` | string | `YYYY-MM-DD` |
| `provider` | string | 可选 |
| `project_id` | string | 可选 |
| `product` | string | 可选 |
| `page` | int | 默认 1 |
| `page_size` | int | 默认 50,最大 500 |
**出参**(真实样例):
```json
[
{
"id": 1759843,
"date": "2026-04-18",
"provider": "azure",
"data_source_id": 2,
"project_id": "45d7a360-af09-40fc-9afc-56dc475245ec",
"project_name": "Xmind运营学习专用2026",
"product": "API Management",
"usage_type": "Consumption Calls",
"region": "southeastasia",
"cost": "0.000000",
"usage_quantity": "0.083300",
"usage_unit": "10K",
"currency": "USD"
}
]
```
字段大致同 `/metering/detail`,额外字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| `project_name` | string \| null | 云厂商侧返回的项目显示名(AWS 未必有;GCP/Azure 多数有)|
`cost` / `usage_quantity` 仍为 **string (Decimal)**。
---
### 4.11 `GET /api/billing/detail/count`
**用途**:与 `billing/detail` 相同筛选下的总行数。
**出参**:`{ "total": 62016 }`
---
## 5. P1 补充接口
### 5.1 Dashboard 维度拆分
模块:`dashboard`,角色:`cloud_viewer` / `cloud_ops` / `cloud_finance`(含 admin)。数据按可见数据源过滤。**所有 cost 字段均为 JSON number(float)**。
#### `GET /api/dashboard/trend`
入参:`start`、`end`(均 `YYYY-MM`);`granularity`(`daily` 默认 / `weekly` / `monthly`)。
出参:`[{ date, cost, cost_by_provider }]`,结构同 §4.3 `trend[]`。
#### `GET /api/dashboard/by-provider`
入参:`month`。出参真实样例:
```json
[
{ "provider": "gcp", "cost": 124735.119318, "percentage": 83.28 },
{ "provider": "azure", "cost": 18058.538325, "percentage": 12.06 }
]
```
| 字段 | 说明 |
|---|---|
| `provider` | `aws`/`gcp`/`azure` |
| `cost` | 当月累计 USD |
| `percentage` | 占当月总费用百分比(两位小数)|
#### `GET /api/dashboard/by-category`
入参:`month`。出参真实样例:
```json
[{ "category_id": 2, "name": "default", "original_cost": 24846.617897,
"markup_rate": 1.15, "final_cost": 28573.61058155 }]
```
| 字段 | 说明 |
|---|---|
| `category_id`、`name` | 费用分类 |
| `original_cost` | 原始成本(未加价)|
| `markup_rate` | 加价率(1.0 = 不加价)|
| `final_cost` | = `original_cost × markup_rate` |
#### `GET /api/dashboard/by-project`
入参:`month`,`limit`(1–100,默认 10)。出参真实样例:
```json
[
{ "project_id": "xianlong-2", "name": "xianlong-2",
"provider": "gcp", "cost": 58749.779907 }
]
```
| 字段 | 说明 |
|---|---|
| `project_id` | **云厂商侧项目 ID(字符串)**,即 GCP project / Azure subscription / AWS account。**不是**本地 `projects.id` |
| `name` | 账单中的项目名(云厂商返回)|
| `provider` | `aws`/`gcp`/`azure` |
| `cost` | 当月累计 USD |
#### `GET /api/dashboard/by-service`
入参:`month`,`provider`(可选),`limit`(1–100)。出参真实样例:
```json
[
{ "product": "Vertex AI", "cost": 124410.727567, "percentage": 83.06 },
{ "product": "Virtual Machines", "cost": 6202.288351, "percentage": 4.14 }
]
```
字段同 `dashboard/by-provider`(`percentage` 相对当月 total)。
#### `GET /api/dashboard/by-region`
入参:`month`。出参真实样例:
```json
[
{ "region": "asia-east1", "provider": "gcp", "cost": 38836.928387 },
{ "region": "asia-southeast1", "provider": "gcp", "cost": 9817.383452 }
]
```
#### `GET /api/dashboard/top-growth`
入参:`period`(默认 `7d`),`limit`(1–50)。出参真实样例:
```json
[
{ "project_id": "lyww-01", "name": "lyww-01",
"current_cost": 3775.070699, "previous_cost": 5e-05,
"growth_pct": 7550141298.0 }
]
```
| 字段 | 说明 |
|---|---|
| `project_id` | 外部项目 ID 字符串 |
| `current_cost` / `previous_cost` | 本周期 / 对比周期累计 USD |
| `growth_pct` | 增幅 %,`previous` 接近 0 时可能非常大 |
#### `GET /api/dashboard/unassigned`
入参:`month`。出参真实样例(未与本地 `projects` 表关联上的外部项目):
```json
[
{ "project_id": "xianlong-2", "name": "xianlong-2",
"provider": "gcp", "cost": 58749.779907, "status": null }
]
```
| 字段 | 说明 |
|---|---|
| `project_id` / `name` / `provider` | 同 by-project |
| `cost` | 当月累计 USD |
| `status` | 总是 `null`(`projects` 表里没有对应行),保留字段以便未来扩展 |
---
### 5.2 `GET /api/service-accounts/`
**模块:`service_accounts`,角色:任意登录**(列表查看所有角色可用)。路径需带尾部斜杠。
**入参(Query)**:`provider`、`status`、`customer_code`(按客户编号反查)、`page`、`page_size`。
**出参**(真实样例):
```json
[
{
"id": 7,
"name": "AWS-Main",
"supply_source_id": 2,
"supplier_name": "神州泰岳",
"provider": "aws",
"external_project_id": "675139393309",
"status": "standby",
"order_method": null,
"customer_codes": [],
"created_at": "2026-04-08T10:19:32.565267"
}
]
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | integer | 本地服务账号主键 |
| `name` | string | 账号显示名 |
| `supply_source_id` / `supplier_name` | integer / string | 所属货源 + 供应商名 |
| `provider` | string | `aws`/`gcp`/`azure`(来自 supply_source)|
| `external_project_id` | string | 云厂商侧 ID(AWS account ID / GCP project / Azure subscription)|
| `status` | string | `active` / `standby` / `inactive` |
| `order_method` | string \| null | 仅 Azure 账号有意义(MCCL-EA / HK CSP 等下单方式)|
| `customer_codes` | string[] | 销售系统下发的客户编号,大写归一化;空数组 = 未分配 |
| `created_at` | string | 创建时间 ISO8601 |
**`status` 派生规则**(后端 `_recompute_status`):
- `inactive`(人工停用)→ 不动,最高优先级
- 否则:`customer_codes` 非空 → `active`;为空 → `standby`
- 触发时机:`PUT /{id}` 带 `customer_codes`、`POST /{id}/activate`、`POST /customer-assignments/sync` 后
---
### 5.3 `GET /api/service-accounts/{account_id}`
**出参**(真实样例,截取):
```json
{
"id": 7,
"name": "AWS-Main",
"supply_source_id": 2,
"supplier_id": 3,
"supplier_name": "神州泰岳",
"provider": "aws",
"external_project_id": "675139393309",
"status": "standby",
"notes": null,
"order_method": null,
"customer_codes": [],
"secret_fields": ["aws_access_key_id", "aws_secret_access_key", "account_id"],
"created_at": "2026-04-08T10:19:32.565267",
"history": [
{ "id": 39, "action": "activated", "from_status": "standby", "to_status": "standby",
"operator": "xiaohei", "customer_code": null, "notes": null,
"created_at": "2026-04-18T19:21:38.081899" },
{ "id": 36, "action": "customer_unbound", "from_status": "active", "to_status": "active",
"operator": "sales", "customer_code": "SYNC_C1", "notes": "sales batch sync",
"created_at": "2026-04-18T19:10:00.000000" }
]
}
```
列表字段同 §5.2。详情额外字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| `supplier_id` | integer | 供应商 FK |
| `notes` | string \| null | 账号备注 |
| `secret_fields` | string[] | 账号凭据字段**名**(不含值);例如 aws_access_key_id、client_secret |
| `history[]` | array | 状态变更 + 客户编号变更历史,按 `created_at DESC` |
| `history[].action` | string | 见下表 |
| `history[].from_status` / `to_status` | string \| null | 仅状态类事件非空 |
| `history[].operator` | string \| null | 触发者用户名;销售系统同步走 `"sales-sync"`/机器账号名 |
| `history[].customer_code` | string \| null | 仅 `customer_*` 事件非空 |
| `history[].notes` | string \| null | 销售批量同步会写 `"sales batch sync"` |
`action` 枚举:
| 值 | 含义 |
|---|---|
| `created` | 创建账号(注:老数据可能记为 `assigned`)|
| `suspended` | 人工停用 |
| `activated` | 从停用/备用恢复 |
| `customer_bound` | 绑定一个客户编号 |
| `customer_unbound` | 解除一个客户编号 |
| `customer_batch_synced` | 销售批量同步(此事件单独记录时使用;通常逐个写 bound/unbound)|
---
### 5.4 `GET /api/service-accounts/{account_id}/costs`
**角色**:任意登录(已放宽)。
**入参(Query)**:`start_date`、`end_date`(必填,`YYYY-MM-DD`)。
**出参**(真实样例,截取):
```json
{
"total_cost": 6991.440645000001,
"total_usage": 5303.442484,
"services": [
{ "service": "Claude Opus 4.6 (Amazon Bedrock Edition)",
"cost": 5280.532382, "usage_quantity": 2690.11845, "usage_unit": "Units" },
{ "service": "Claude Sonnet 4.6 (Amazon Bedrock Edition)",
"cost": 1246.2185079999997, "usage_quantity": 1582.6575039999998, "usage_unit": "Units" }
],
"daily": [
{ "date": "2026-04-01", "cost": 45.23, "usage_quantity": 12.0 }
],
"daily_by_service": [
{ "date": "2026-04-01", "service": "AWS Cost Explorer",
"cost": 0.01, "usage_quantity": 1.0, "usage_unit": "Requests" }
]
}
```
**本接口的 cost/usage 是 JSON number(float)**(与 `/metering/*` 不同)。
| 字段 | 说明 |
|---|---|
| `total_cost` / `total_usage` | 本账号在 date 区间内的汇总 |
| `services[].service` | 产品名(对应 billing_data.product)|
| `services[].usage_unit` | 该产品的用量单位;该产品有多个单位时,后端取其一 |
| `daily[]` | 按日费用+用量合计 |
| `daily_by_service[]` | 按日 × 服务拆分(用于堆叠图)|
---
### 5.5 `GET /api/service-accounts/daily-report`
**角色:任意登录**(前端『统计』页调用,viewer/ops/finance 都可访问)。
**入参(Query)**:`start_date`、`end_date`(必填),`provider`(可选)。
**出参**(真实样例):
```json
[
{
"account_id": 88,
"account_name": "chuhai 自用",
"provider": "azure",
"external_project_id": "09e4b3a6-8159-4f14-b108-e4a18ace9212",
"date": "2026-04-01",
"product": "Foundry Models",
"cost": 2.438673
}
]
```
每行 = (账号, 日期, 产品) 聚合后的 cost,**JSON number(float)**。按 `date`→`external_project_id`→`product` 排序。
| 字段 | 说明 |
|---|---|
| `account_id` | 本地 `projects.id` |
| `account_name` | 账号显示名(本地 `projects.name`)|
| `provider` | `aws`/`gcp`/`azure` |
| `external_project_id` | 云厂商侧 ID |
| `date` / `product` | 账单日期 + 服务 |
| `cost` | USD |
> 注:此接口的"每条记录按 (账号, 日期, 产品) 聚合";业务含义是前端『统计』页。物理挂在 `service_accounts` 路由下,但已做端点级权限区分——只读端点(列表 / 详情 / costs / daily-report)任意登录可用,敏感端点(创建/删除/暂停/恢复/凭据/批量同步)需 `cloud_admin`。
---
### 5.6 `GET /api/projects/` 与 `GET /api/projects/{project_id}`
**模块:`projects`,角色:任意登录(读);写需 `cloud_admin`**。
`/api/projects` 是老的 Project CRUD,**前端不使用**。和 `/api/service-accounts` 指向同一个 `projects` 表,但返回字段和交互语义略有差异。AI 通常调 `service-accounts/` 即可;这组接口仅供历史集成。
**列表入参**:`status`、`provider`、`page`、`page_size`。
**出参**(真实样例,单对象):
```json
{
"id": 7,
"name": "AWS-Main",
"supply_source_id": 2,
"provider": "aws",
"supplier_name": "神州泰岳",
"external_project_id": "675139393309",
"data_source_id": 1,
"category_id": 2,
"status": "standby",
"notes": null,
"created_at": "2026-04-08T10:19:32.565267",
"updated_at": "2026-04-18T18:53:30.265359"
}
```
| 字段 | 说明 |
|---|---|
| `data_source_id` | FK → data_sources(对应 /api/data-sources/ 条目)|
| `category_id` | FK → categories(费用分类 markup_rate);可为 null |
| `updated_at` | 最近一次 update 时间 |
| 其它字段 | 同 §5.2 |
> 注意:本接口**不返回** `customer_codes`、`order_method`、`history`。需要这些用 `/api/service-accounts/{id}`。
---
### 5.7 `GET /api/bills/`
**模块:`bills`,角色:`cloud_finance`(含 admin)**。
**入参(Query)**:`month`(`YYYY-MM`)、`status`、`page`、`page_size`。
**出参**:实际调用该账号当月为空 `[]`(尚未生成账单)。字段表(来自后端 schema):
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | integer | 账单 id |
| `month` | string | `YYYY-MM` |
| `category_id` | integer | 费用分类 FK |
| `provider` | string | aws/gcp/azure |
| `original_cost` | string/number | 原始成本 USD |
| `markup_rate` | string/number | 加价率(1.0=不加价)|
| `final_cost` | string/number | 加价后金额 |
| `adjustment` | string/number | 人工调整额(正负皆可)|
| `status` | string | `draft` / `confirmed` / `paid` |
| `confirmed_at` | string \| null | 确认时间 |
| `notes` | string \| null | 备注 |
| `created_at` | string | 创建时间 |
---
### 5.8 `GET /api/bills/{bill_id}`
单张月度账单详情(同上结构,单对象)。
---
### 5.9 `GET /api/alerts/rule-status`
**模块:`alerts`,角色:任意登录**。
**入参(Query)**:`month`(可选,`YYYY-MM`,默认当月)。
**出参**:空数组 `[]`(该环境当前未配置告警规则)。字段表:
| 字段 | 类型 | 说明 |
|---|---|---|
| `rule_id` | integer | 规则 ID |
| `rule_name` | string | 规则显示名 |
| `threshold_type` | string | `monthly_budget` / `daily_budget` / `growth_rate` / `monthly_minimum_commitment` 等 |
| `threshold_value` | number | 阈值 |
| `actual` | number | 当前实际值 |
| `pct` | number | `actual / threshold_value × 100`(增长率类型为差额 %)|
| `triggered` | boolean | 是否触发 |
| `account_name` | string | 关联的服务账号名(可能为 null,表示全局规则)|
| `provider` | string | aws/gcp/azure |
| `external_project_id` | string | 云厂商侧 ID |
---
### 5.10 `GET /api/suppliers/supply-sources/all`
**模块:`suppliers`,角色:`cloud_admin`**(整个 suppliers 模块都是 admin)。
**入参**:`supplier_id`(可选,过滤某个供应商)。
**出参**(真实样例):
```json
[
{ "id": 3, "supplier_id": 1, "supplier_name": "长虹佳华",
"provider": "azure", "account_count": 3 },
{ "id": 5, "supplier_id": 2, "supplier_name": "内部测试专用",
"provider": "gcp", "account_count": 1 }
]
```
| 字段 | 说明 |
|---|---|
| `id` | 货源 ID(`supply_sources.id`)|
| `supplier_id` / `supplier_name` | 所属供应商 |
| `provider` | 该货源对应的云 |
| `account_count` | 该货源下的服务账号数 |
---
### 5.11 `GET /api/metering/products`
**用途**:产品去重列表(下拉/消歧)。
**入参**:`provider`、`account_id`、`supply_source_id`、`supplier_name`、`data_source_id`。
**出参**(真实样例,截取):
```json
[
{ "product": "Amazon Simple Notification Service" },
{ "product": "Amazon Simple Queue Service" },
{ "product": "Vertex AI" }
]
```
单字段 `product`,排重 + 排序。用于前端 Metering 页的服务过滤下拉。
---
## 6. P2 可选接口
### 6.1 `GET /api/categories/`
**角色:`cloud_admin`**(整个 categories 模块 admin-only)。
**出参**(真实样例):
```json
[
{ "id": 1, "name": "????", "markup_rate": "1.0000",
"description": "test",
"created_at": "2026-04-08T03:43:02.546432",
"updated_at": "2026-04-08T03:43:02.546432" },
{ "id": 2, "name": "default", "markup_rate": "1.1500",
"description": "default channel",
"created_at": "2026-04-08T03:43:28.568331",
"updated_at": "2026-04-08T10:23:02.934844" }
]
```
| 字段 | 类型 | 说明 |
|---|---|---|
| `markup_rate` | **string**(Decimal,4 位小数)| 加价率 |
| `name` | string | 分类名;历史数据可能含占位符 |
### 6.2 `GET /api/exchange-rates/`
**角色:任意登录**(读)。入参:`date`(可选)、`from_currency`。
**出参**:数组形如 `[{ from_currency, to_currency, rate, date }]`;该环境返回 `[]`。
### 6.3 `GET /api/data-sources/`
**角色:`cloud_admin`**。
**出参**(真实样例):
```json
[
{
"id": 1,
"name": "AWS-675139393309",
"cloud_account_id": 1,
"category_id": 2,
"config": { "account_id": "675139393309" },
"last_sync_at": "2026-04-18T18:00:19.046951",
"sync_status": "success",
"is_active": true,
"created_at": "2026-04-08T03:48:09.812203"
}
]
```
| 字段 | 说明 |
|---|---|
| `config` | 采集器配置 JSONB;Azure 会有 `subscription_id`/`collect_mode`/`cost_metric`;AWS 仅 `account_id` |
| `last_sync_at` / `sync_status` | 最近一次同步时间与结果 |
### 6.4 `GET /api/resources/` + `GET /api/resources/{id}`
**角色:任意登录**(读)。入参:`provider`、`project_id`、`resource_type`、`page`、`page_size`。数据按可见数据源过滤。该环境返回 `[]`。字段表(资源清单 schema):`id`、`provider`、`resource_type`、`resource_id`、`name`、`region`、`project_id`、`monthly_cost`(number,最近一个月估算)、`tags`。
### 6.5 `GET /api/billing/export` / `/api/metering/export`
CSV 流式下载,大流量,适合工具落盘,不建议作为模型上下文。入参同 `detail`,响应 `Content-Type: text/csv`。
---
## 7. 销售系统专用接口(客户编号下发)
> 面向"销售系统对接",**AI 大脑不调用**。模块:`service_accounts`;**这两条是写操作,需 `cloud_admin`**(`service_accounts` 只读端点已放宽到任意登录,但写仍然 admin-only)。建议通过 Casdoor `client_credentials` 拿 token。
### 7.1 `POST /api/service-accounts/customer-assignments/sync`
**用途**:销售系统把"客户编号 ↔ 服务账号"关联批量下发。幂等。
定位键:`(supplier_name, provider, external_project_id)`。匹配不到的记录进 `unmatched` 返回,不阻断整批。
**入参(Body)**:
```json
{
"mode": "patch",
"scope_customer_codes": ["C001", "C002"],
"assignments": [
{
"customer_code": "C001",
"supplier_name": "xxx 供应商",
"provider": "azure",
"external_project_id": "5550d5e0-56d3-46b2-8c08-bb9834d8b349"
}
]
}
```
- `mode=patch`:只做 upsert,从不删除。推荐作为默认模式。
- `mode=full`:对 `scope_customer_codes` 这批做差分(多删少插)。若该字段留空,后端回退为"按 `assignments` 里出现的客户编号作为 scope"——这种隐式行为建议明确传一份。
- `customer_code` 自动 `upper().strip()` 归一化。
**出参**:
```json
{
"inserted": 1,
"deleted": 0,
"unchanged": 0,
"unmatched": [
{
"customer_code": "C002",
"supplier_name": "bogus",
"provider": "azure",
"external_project_id": "does-not-exist",
"reason": "service account not found"
}
]
}
```
### 7.2 `PUT /api/service-accounts/{account_id}`(仅 `customer_codes` 字段)
**用途**:单个服务账号的客户编号**全量覆盖**(和 `secret_data` 同语义:`undefined` 不动;`[]` 清空;`[...]` 替换)。
**入参(Body)**:
```json
{ "customer_codes": ["C001"] }
```
**出参**:整个 `ServiceAccountDetail`(见 §5.3)。
副作用:
- 新增/删除会写 `project_assignment_logs`,action=`customer_bound` / `customer_unbound`,带 operator。
- 清空或首次绑定都会触发 `_recompute_status`,自动把 status 从 `active` → `standby` 或反向切换。
---
## 8. 禁止对 AI 开放的接口
以下接口 **不应** 加入 AI 可调工具列表:
| 类别 | 路径 | 原因 |
|---|---|---|
| 凭据明文 | `GET /api/service-accounts/{id}/credentials` | 会解密云账号凭据 |
| 写操作 | 所有 `POST` / `PUT` / `PATCH` / `DELETE` | 含同步触发、账单调整、告警规则变更、服务账号变更等 |
| 客户编号分配(销售专用) | `POST /api/service-accounts/customer-assignments/sync`、`PUT /api/service-accounts/{id}` 带 `customer_codes` | 仅销售系统走 Casdoor client_credentials 下发;AI 不应写 |
| 同步触发 | `/api/sync/*`(除 `GET /last`) | 需 `cloud_ops`,触发后台任务 |
| Azure 部署 | `/api/azure-deploy/*` | 需 `cloud_admin`,涉及 ARM Token 与资源创建 |
| 跨租户授权 | `/api/azure-consent/*` | 需 `cloud_admin`,改订阅授权态 |
| 认证管理 | `/api/admin/users/*` · `/api/api-keys/*` · `/api/api-permissions/*` | 需 `cloud_admin`,管理员专用 |
`service_accounts` 模块混合了只读和凭据接口,如 AI 需要 §5.2–5.5 的数据,**工具清单不得包含 `/credentials`**。
---
## 9. 建议调用顺序
1. `GET /api/health` → 连通性探测
2. `GET /api/auth/me` → 确认身份与可见范围
3. `GET /api/sync/last` → 数据新鲜度
4. `GET /api/dashboard/bundle` 或 `metering/summary` → 总览
5. 钻取 → `metering/detail` + `detail/count` 分页,或 `billing/detail`
6. 业务主体 → `service-accounts/` 或 `projects/`
7. 是否超支 → `alerts/rule-status`
---
## 10. 各模块权限速查
| 模块 | URL 前缀 | 所需角色 | 数据范围过滤 |
|------|----------|----------|-------------|
| `dashboard` | `/api/dashboard/*` | viewer / ops / finance(含 admin) | 按可见数据源 |
| `billing` | `/api/billing/*` | 任意登录 | 按可见数据源 |
| `metering` | `/api/metering/*` | 任意登录 | 支持筛选参数 |
| `bills` | `/api/bills/*` | `cloud_finance`(含 admin) | 无 |
| `sync` | `/api/sync/*` | `cloud_ops`(含 admin) | 无 |
| `cloud_accounts` | `/api/cloud-accounts/*` | 读=任意登录;写=`cloud_admin` | 按可见云账号 |
| `resources` | `/api/resources/*` | 任意登录 | 按可见数据源 |
| `projects` | `/api/projects/*` | 读=任意登录;写=`cloud_admin` | 无 |
| `alerts` | `/api/alerts/*` | 读=任意登录;写告警规则=`cloud_ops` | 无 |
| `categories` | `/api/categories/*` | **`cloud_admin`**(整个模块)| 无 |
| `suppliers` | `/api/suppliers/*` | **`cloud_admin`**(整个模块)| 无 |
| `exchange_rates` | `/api/exchange-rates/*` | 读=任意登录;写=`cloud_admin` / `cloud_finance` | 无 |
| `data_sources` | `/api/data-sources/*` | **`cloud_admin`**(整个模块)| 无 |
| `service_accounts` | `/api/service-accounts/*` | 读=任意登录;写=`cloud_admin` | 无 |
| `azure_deploy` | `/api/azure-deploy/*` | `cloud_admin` | 无 |
| `azure_consent` | `/api/azure-consent/*` | `cloud_admin` | 无 |
**匿名可访问**(不需要任何凭据):`/api/health`、`/api/auth/*`、`/docs`、`/redoc`、`/openapi.json`
---
## 11. OpenAPI
运行时通过 `GET /openapi.json` 或 `/docs` 获取与部署版本一致的 Schema(匿名可访问)。若本文与 OpenAPI 冲突,**以 OpenAPI 为准**。