monitoring_api.md 13 KB

监控大屏 API 接入文档

1. 概述

监控大屏 API 提供树状层级的消费数据汇总,用于平台监控大屏展示。

层级结构:平台 → 超级管理员 → 租户 → 用户

每条消费记录包含三级折扣及对应金额:用户折扣企业折扣超管折扣


2. 部署准备

2.1 执行数据库迁移

-- 迁移文件:migrations/060_create_super_admin_tenant_table.sql
psql -d your_database -f migrations/060_create_super_admin_tenant_table.sql

-- 迁移文件:migrations/063_create_super_admin_model_discount_table.sql
psql -d your_database -f migrations/063_create_super_admin_model_discount_table.sql

2.2 初始化超级管理员-租户关联数据

迁移执行后,需要往 super_admin_tenant 表写入关联关系:

-- 示例:将租户 1、2、3 分配给超级管理员 1
INSERT INTO aigcspace.super_admin_tenant (super_admin_id, tenant_id) VALUES
(1, 1),
(1, 2),
(1, 3);

-- 将租户 4、5 分配给超级管理员 2
INSERT INTO aigcspace.super_admin_tenant (super_admin_id, tenant_id) VALUES
(2, 4),
(2, 5);

注意:未关联到任何超级管理员的租户会归入"未分配租户"节点展示。

2.3 同步超管折扣

超管折扣从 crawler API 同步到数据库,可通过爬虫自动同步或手动触发:

# 手动同步(爬虫同步时会自动写入超管折扣表)
python -c "from app.services.crawler_sync_service import _fetch_crawler_discounts_for_sync; print(_fetch_crawler_discounts_for_sync(db))"

或通过超管后台接口手动同步:

POST /api/super/super-admin/discounts/sync

2.4 重启后端服务

# 开发环境
python main.py

# 生产环境
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8010

3. API 接口

3.1 获取监控大屏数据

GET /api/public/monitoring/dashboard

公共接口,无需鉴权

请求参数

参数 类型 必填 说明
start_date string 查询开始日期,格式 YYYY-MM-DD,不传则查全部
end_date string 查询结束日期,格式 YYYY-MM-DD,不传则查全部
super_admin_id integer 指定某个超级管理员ID,不传则查全部

请求示例

# 查询全部
curl -X GET "http://localhost:8010/api/public/monitoring/dashboard"

# 按时间范围查询
curl -X GET "http://localhost:8010/api/public/monitoring/dashboard?start_date=2026-01-01&end_date=2026-05-12"

# 查询某个超级管理员
curl -X GET "http://localhost:8010/api/public/monitoring/dashboard?super_admin_id=1"

响应结构

{
  "overview": {
    "total_super_admins": 3,
    "total_tenants": 15,
    "total_users": 200,
    "total_consumption": "12500.0000",
    "total_tenant_charged": "9800.0000",
    "total_balance": "50000.0000"
  },
  "super_admins": [
    {
      "super_admin_id": 1,
      "username": "admin1",
      "nickname": "管理员A",
      "tenant_count": 5,
      "total_consumption": "8000.0000",
      "total_tenant_charged": "6400.0000",
      "tenants": [
        {
          "tenant_id": 1,
          "company_name": "成都网讯",
          "subdomain": "wangxun",
          "total_consumption": "3000.0000",
          "total_tenant_charged": "2400.0000",
          "balance": "10000.0000",
          "user_count": 20,
          "users": [
            {
              "user_id": "u001",
              "username": "zhangsan",
              "nickname": "张三",
              "total_consumption": "500.0000",
              "tenant_actual_total": "400.0000",
              "consumption_records": [
                {
                  "user_id": "u001",
                  "username": "zhangsan",
                  "tenant_name": "成都网讯",
                  "order_no": "20260508194831_ai_conversation_xxx",
                  "model_name": "通义千问Plus",
                  "model_code": "qwen-plus",
                  "amount": "0.0040",
                  "created_at": "2026-05-08T19:48:31",
                  "invoiced": false,
                  "user_discount": "0.9000",
                  "user_actual_price": "0.0040",
                  "tenant_discount": "0.8000",
                  "tenant_actual_price": "0.0036",
                  "super_admin_discount": "0.8000",
                  "super_admin_actual_price": "0.0036"
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "start_date": "2026-01-01",
  "end_date": "2026-05-12"
}

4. 字段说明

4.1 平台汇总 (overview)

字段 类型 说明
total_super_admins integer 超级管理员数量
total_tenants integer 租户总数
total_users integer 用户总数
total_consumption string(Decimal) 平台总消费金额(元),即所有用户实际支付之和
total_tenant_charged string(Decimal) 平台向企业收取总额(元),即用户实际支付 / 用户折扣 × 企业折扣
total_balance string(Decimal) 所有企业当前余额合计(元)

4.2 超级管理员节点 (super_admins[])

字段 类型 说明
super_admin_id integer 超级管理员ID
username string 管理员用户名
nickname string 管理员昵称
tenant_count integer 管辖的租户数量
total_consumption string(Decimal) 管辖范围内所有用户消费总额
total_tenant_charged string(Decimal) 管辖范围内平台向企业收取总额
tenants array 管辖的租户列表

过滤规则:该超管管辖范围内没有任何有消费记录的租户时,该节点仍会返回但 tenants 为空数组。

4.3 租户节点 (tenants[])

字段 类型 说明
tenant_id integer 租户ID
company_name string 企业名称
subdomain string 二级域名前缀
total_consumption string(Decimal) 该租户下所有用户消费总额
total_tenant_charged string(Decimal) 平台向该企业收取的总额
balance string(Decimal) 企业当前余额
user_count integer 用户数量
users array 用户消费列表(仅包含有消费记录的用户)

过滤规则:租户下所有用户在查询时间范围内均无消费记录时,该租户节点被过滤不返回。

4.4 用户节点 (users[])

字段 类型 说明
user_id string 用户ID
username string 用户名
nickname string 用户昵称
total_consumption string(Decimal) 用户累计消费(用户实际支付金额)
tenant_actual_total string(Decimal) 企业为该用户实际被平台收取的总额
consumption_records array 该用户的消费记录流水(扁平列表)

过滤规则:用户在查询时间范围内无消费记录时,该用户节点被过滤不返回。

4.5 消费记录流水 (consumption_records[])

每条记录包含用户信息、消费明细和三级折扣及金额

字段 类型 说明
user_id string 用户ID
username string 用户名
tenant_name string 所属租户名称(NULL表示平台直属用户)
order_no string 订单号(对应 balance_log.biz_order_no)
model_name string 模型显示名称
model_code string 模型标识(如 qwen-plus
amount string(Decimal) 消费金额(用户实际支付)
created_at string(datetime) 消费时间
invoiced boolean 是否已开票
user_discount string(Decimal) 用户折扣率(0~1,如 0.9 表示9折)
user_actual_price string(Decimal) 用户实际支付金额
tenant_discount string(Decimal) 企业折扣率(0~1,如 0.8 表示8折)
tenant_actual_price string(Decimal) 企业实际支付金额
super_admin_discount string(Decimal) 超级管理员折扣率(从 crawler API 同步)
super_admin_actual_price string(Decimal) 平台向超管收取金额

5. 金额关系说明

5.1 三级折扣体系

平台原价
  │
  ├── 超管折扣率 (super_admin_discount) → 平台向超管收取 (super_admin_actual_price)
  │      = amount / user_discount × super_admin_discount
  │
  ├── 企业折扣率 (tenant_discount) → 企业实际支付 (tenant_actual_price)
  │      = amount / user_discount × tenant_discount
  │
  └── 用户折扣率 (user_discount) → 用户实际支付 (user_actual_price)
         = amount(user_consumption.amount 已为用户折扣后的价格)

计算公式

层级 折扣率来源 实际支付计算
用户 user_model_discount user_actual_price = amount
企业 tenant_model_discount tenant_actual_price = amount / user_discount × tenant_discount
超管 super_admin_model_discount 表(crawler 同步) super_admin_actual_price = amount / user_discount × super_admin_discount

关键区别

  • user_actual_price:用户实际掏的钱(用户余额扣减)
  • tenant_actual_price:平台从企业账户扣的钱(企业余额扣减)
  • super_admin_actual_price:平台向超管收取的金额
  • 三者之差 = 各层级的折扣让利空间

5.2 超管折扣同步

超管折扣存储在 super_admin_model_discount 表中,通过爬虫同步服务自动更新:

  • 全局折扣:model_code = "*"
  • 模型级折扣:model_code = 具体模型标识
  • 优先匹配具体模型,无匹配则使用全局折扣 *
  • 爬虫同步时写入,监控接口直接读库,响应速度快

6. 前端接入示例

6.1 Vue 3 数据获取

import { ref, onMounted } from 'vue'

const dashboardData = ref(null)

async function fetchDashboard(startDate, endDate) {
  const params = new URLSearchParams()
  if (startDate) params.append('start_date', startDate)
  if (endDate) params.append('end_date', endDate)

  const res = await fetch(`/api/public/monitoring/dashboard?${params}`)
  dashboardData.value = await res.json()
}

6.2 消费记录表格渲染

// 扁平化所有用户的消费记录
function flattenRecords(data) {
  const records = []
  for (const sa of data.super_admins) {
    for (const tenant of sa.tenants) {
      for (const user of tenant.users) {
        for (const record of user.consumption_records) {
          records.push(record)
        }
      }
    }
  }
  return records.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
}

6.3 按模型聚合

// 从消费记录中按模型聚合
function aggregateByModel(dashboardData) {
  const modelMap = {}

  for (const sa of dashboardData.super_admins) {
    for (const tenant of sa.tenants) {
      for (const user of tenant.users) {
        for (const record of user.consumption_records) {
          const key = record.model_code
          if (!modelMap[key]) {
            modelMap[key] = {
              model_code: record.model_code,
              model_name: record.model_name,
              total_amount: 0,
              user_total: 0,
              tenant_total: 0,
              super_admin_total: 0,
              count: 0
            }
          }
          modelMap[key].total_amount += parseFloat(record.amount)
          modelMap[key].user_total += parseFloat(record.user_actual_price)
          modelMap[key].tenant_total += parseFloat(record.tenant_actual_price)
          modelMap[key].super_admin_total += parseFloat(record.super_admin_actual_price)
          modelMap[key].count += 1
        }
      }
    }
  }

  return Object.values(modelMap).sort((a, b) => b.total_amount - a.total_amount)
}

7. 常见问题

Q: 为什么有些用户/租户不返回?

A: 接口默认过滤 total_consumption = 0 的用户和租户。只有查询时间范围内有真实消费记录的节点才会返回。

Q: 未分配租户是什么意思?

A: 如果某个租户没有在 super_admin_tenant 表中关联任何超级管理员,该租户会归入 super_admin_id=0, nickname="未分配租户" 的特殊节点。

Q: 折扣率是 1.0 代表什么?

A: 折扣率 1.0 表示没有折扣(原价)。0.8 表示8折,用户只需支付原价的80%。

Q: 超管折扣如何更新?

A: 超管折扣通过爬虫服务 sync_from_crawler 自动同步。爬虫每次同步模型数据时,会将折扣写入 super_admin_model_discount 表。也可通过超管后台 POST /api/super/super-admin/discounts/sync 手动触发同步。

Q: 时间范围如何影响数据?

A: start_dateend_date 过滤 user_consumption 表的 created_at 字段。企业余额是实时快照不受时间过滤。

Q: 返回数据量很大怎么办?

A: 可以通过 super_admin_id 参数只查某个管理员的数据,减少返回量。