From 65ad7657ef92033e463fa63d8e4ed8c261307bfe Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Fri, 25 Apr 2025 18:42:59 +0800 Subject: [PATCH 01/29] feature/1.8-Improve system observability --- .../src/components/aoplatform/InsidePage.tsx | 11 +- .../aoplatform/TimeRangeSelector.tsx | 6 +- .../components/aoplatform/serviceInfoCard.tsx | 293 ++++++++ .../common/src/locales/keyHashMap.json | 39 +- .../common/src/locales/scan/en-US.json | 31 +- .../common/src/locales/scan/ja-JP.json | 31 +- .../src/locales/scan/newJson/zh-CN.json | 70 +- .../common/src/locales/scan/zh-CN.json | 29 +- .../common/src/locales/scan/zh-TW.json | 31 +- .../core/src/const/ai-service/type.ts | 2 +- frontend/packages/core/src/const/const.tsx | 28 + .../packages/core/src/const/system/const.tsx | 122 ++++ frontend/packages/core/src/index.css | 16 + .../pages/aiService/AiServiceInsidePage.tsx | 19 +- .../serviceLogs/AiServiceLogsContainer.tsx | 6 + .../serviceLogs/ApiNetWorkDataPreview.tsx | 89 +++ .../core/src/pages/serviceLogs/LogDetail.tsx | 424 ++++++++++++ .../serviceLogs/RestServiceLogsContainer.tsx | 7 + .../src/pages/serviceLogs/ServiceLogs.tsx | 282 ++++++++ .../serviceOverview/AiServiceContainer.tsx | 7 + .../serviceOverview/RestServiceContainer.tsx | 12 + .../charts/ServiceAreaChart.tsx | 121 ++++ .../serviceOverview/charts/ServiceBarChar.tsx | 174 +++++ .../filter/DateSelectFilter.tsx | 37 + .../serviceOverview/indicator/Indicator.tsx | 86 +++ .../rankingList/RankingList.tsx | 91 +++ .../pages/serviceOverview/serviceOverview.tsx | 541 +++++++++++++++ .../core/src/pages/serviceOverview/utils.ts | 58 ++ .../src/pages/system/SystemInsidePage.tsx | 37 +- .../dashboard/src/pages/DashboardTotal.tsx | 649 +++++++++++++++--- .../src/pages/serviceHub/ServiceHubDetail.tsx | 154 +---- 31 files changed, 3161 insertions(+), 342 deletions(-) create mode 100644 frontend/packages/common/src/components/aoplatform/serviceInfoCard.tsx create mode 100644 frontend/packages/core/src/pages/serviceLogs/AiServiceLogsContainer.tsx create mode 100644 frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx create mode 100644 frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx create mode 100644 frontend/packages/core/src/pages/serviceLogs/RestServiceLogsContainer.tsx create mode 100644 frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/AiServiceContainer.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/RestServiceContainer.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/filter/DateSelectFilter.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx create mode 100644 frontend/packages/core/src/pages/serviceOverview/utils.ts diff --git a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx index 68b0143d..44f8bdb7 100644 --- a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx +++ b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx @@ -26,6 +26,7 @@ class InsidePageProps { scrollInsidePage?: boolean = false customPadding?: boolean customBtn?: ReactNode + customBanner?: ReactNode } const InsidePage: FC = ({ @@ -46,7 +47,8 @@ const InsidePage: FC = ({ scrollPage = true, scrollInsidePage = false, customPadding = false, - customBtn + customBtn, + customBanner }) => { const navigate = useNavigate() @@ -61,8 +63,13 @@ const InsidePage: FC = ({
- {!pageTitle && !description && !backUrl && !customBtn ? ( + {!pageTitle && !description && !backUrl && !customBtn && !customBanner ? ( <> + ) : customBanner ? ( +
+ {backUrl && goBack()} />} + {customBanner} +
) : (
{backUrl && goBack()} />} diff --git a/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx b/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx index f2724dd8..56dbba6e 100644 --- a/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx +++ b/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx @@ -27,6 +27,7 @@ type TimeRangeSelectorProps = { bindRef?: any hideBtns?: TimeRangeButton[] defaultTimeButton?: TimeRangeButton + customClassNames?: string } const TimeRangeSelector = (props: TimeRangeSelectorProps) => { const { @@ -38,7 +39,8 @@ const TimeRangeSelector = (props: TimeRangeSelectorProps) => { labelSize = 'default', bindRef, hideBtns = [], - defaultTimeButton = 'hour' + defaultTimeButton = 'hour', + customClassNames = 'pt-btnybase' } = props const [timeButton, setTimeButton] = useState(initialTimeButton || '') const [datePickerValue, setDatePickerValue] = useState(initialDatePickerValue || [null, null]) @@ -111,7 +113,7 @@ const TimeRangeSelector = (props: TimeRangeSelectorProps) => { } return ( -
+
{!hideTitle && } {hideBtns?.length && hideBtns.includes('hour') ? null : ( diff --git a/frontend/packages/common/src/components/aoplatform/serviceInfoCard.tsx b/frontend/packages/common/src/components/aoplatform/serviceInfoCard.tsx new file mode 100644 index 00000000..926969f7 --- /dev/null +++ b/frontend/packages/common/src/components/aoplatform/serviceInfoCard.tsx @@ -0,0 +1,293 @@ +import { Avatar, Button, Card, Tag, Tooltip, App } from 'antd' +import { Icon } from '@iconify/react/dist/iconify.js' +import { $t } from '@common/locales/index.ts' +import { ApiOutlined } from '@ant-design/icons' +import { useEffect, useState } from 'react' +import { SERVICE_KIND_OPTIONS } from '@core/const/system/const' +import { IconButton } from '@common/components/postcat/api/IconButton' +import useCopyToClipboard from '@common/hooks/copy' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { useFetch } from '@common/hooks/http' + +export type ServiceBasicInfoType = { + id?: string + logo?: string + name: string + description: string + appNum: number + apiNum: number + serviceName: string + serviceDesc: string + invokeCount: number + catalogue: { + name: string + } + serviceKind: string + service_kind: string + enableMcp: boolean + enable_mcp: boolean + isReleased?: boolean +} + +type ServiceInfoCardProps = { + actionSlot?: React.ReactNode + customClassName?: string + serviceId?: string + serviceBasicInfo?: ServiceBasicInfoType + teamId?: string +} +const ServiceInfoCard = ({ + actionSlot, + customClassName, + serviceId, + serviceBasicInfo, + teamId +}: ServiceInfoCardProps) => { + /** 服务指标 */ + const [serviceMetrics, setServiceMetrics] = useState<{ title: string; icon: React.ReactNode; value: string }[]>([]) + /** 服务标签 */ + const [serviceTags, setServiceTags] = useState< + { color: string; textColor: string; title: string; content: React.ReactNode }[] + >([]) + /** 剪切板 */ + const { copyToClipboard } = useCopyToClipboard() + /** 弹窗组件 */ + const { message } = App.useApp() + /** 获取服务信息 */ + const { fetchData } = useFetch() + /** 服务信息 */ + const [serviceOverview, setServiceOverview] = useState() + + /** + * 复制 + * @param value + * @returns + */ + const handleCopy = async (value: string): Promise => { + if (value) { + copyToClipboard(value) + message.success($t(RESPONSE_TIPS.copySuccess)) + } + } + + /** 获取服务信息 */ + const getServiceOverview = () => { + fetchData>('service/overview/basic', { + method: 'GET', + eoParams: { service: serviceId, team: teamId }, + eoTransformKeys: [ + 'enable_mcp', + 'service_kind', + 'subscriber_num', + 'invoke_num', + 'avaliable_monitor', + 'is_released' + ], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + ...data.overview, + appNum: data.overview.subscriberNum, + invokeCount: data.overview.invokeNum, + serviceName: data.overview.name, + serviceDesc: data.overview.description + } + setServiceOverview(serviceOverview) + setServiceMetricsList(serviceOverview) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } + + /** + * 打开服务详情页面 + */ + const openInPortal = () => { + window.open(`/serviceHub/detail/${serviceOverview?.id}`, '_blank') + } + + // 格式化调用次数,添加K和M单位 + const formatInvokeCount = (count: number | null | undefined): string => { + if (count === null || count === undefined) return '-' + if (count >= 1000000) { + const value = Math.floor(count / 100000) / 10 + return `${value}M` + } + if (count >= 1000) { + const value = Math.floor(count / 100) / 10 + return `${value}K` + } + return count.toString() + } + + const setServiceMetricsList = (serviceOverview: ServiceBasicInfoType) => { + // 设置服务指标数据 + setServiceMetrics([ + { + title: 'API 数量', + icon: , + value: serviceOverview.apiNum?.toString() || '0' + }, + { + title: '接入消费者数量', + icon: , + value: serviceOverview.appNum?.toString() || '0' + }, + { + title: '30天内调用次数', + icon: , + value: formatInvokeCount(serviceOverview.invokeCount ?? 0) + } + ]) + const serviceKind = serviceOverview?.serviceKind || serviceOverview?.service_kind + // 设置服务标签数据 + const tags = [ + { + color: '#7371fc1b', + textColor: 'text-theme', + title: serviceOverview?.catalogue?.name || '-', + content: serviceOverview?.catalogue?.name || '-' + }, + { + color: `#${serviceKind === 'ai' ? 'EADEFF' : 'DEFFE7'}`, + textColor: 'text-[#000]', + title: serviceKind || '-', + content: SERVICE_KIND_OPTIONS.find((x) => x.value === serviceKind)?.label || '-' + } + ] + + // 如果启用了MCP,添加MCP标签 + if (serviceOverview?.enableMcp) { + tags.push({ + color: '#FFF0C1', + textColor: 'text-[#000]', + title: 'MCP', + content: 'MCP' + }) + } + + setServiceTags(tags) + } + useEffect(() => { + if (!serviceId && serviceBasicInfo) { + setServiceMetricsList(serviceBasicInfo) + setServiceOverview(serviceBasicInfo) + return + } + getServiceOverview() + }, [serviceId, serviceBasicInfo]) + return ( + <> + + {serviceOverview && ( + <> +
+
+
+ + ) : undefined + } + icon={serviceOverview.logo ? '' : } + > + {' '} + +
+
+

+ {serviceOverview.serviceName} +

+
+ {serviceTags.map((tag, index) => ( + + {tag.content} + + ))} + {serviceMetrics.map((item, index) => ( + + + {item.icon} + {item.value} + + + ))} +
+
+ {serviceOverview.id && ( + <> +
+ + {$t('服务 ID')}:{serviceOverview.id || '-'} + handleCopy(serviceOverview.id || '')} + sx={{ + position: 'absolute', + top: '0px', + right: '5px', + color: '#999', + transition: 'none', + '&.MuiButtonBase-root:hover': { + background: 'transparent', + color: '#3D46F2', + transition: 'none' + } + }} + > + + + + +
+ + )} +
+ + {serviceOverview.serviceDesc || $t('暂无服务描述')} + +
+ + )} +
{actionSlot}
+
+ + ) +} + +export default ServiceInfoCard diff --git a/frontend/packages/common/src/locales/keyHashMap.json b/frontend/packages/common/src/locales/keyHashMap.json index dbdecb5f..1d283fed 100644 --- a/frontend/packages/common/src/locales/keyHashMap.json +++ b/frontend/packages/common/src/locales/keyHashMap.json @@ -54,6 +54,10 @@ "上游列表": "K54e44357", "备注": "Kb8e8e6f5", "上线情况": "K7e52ffa3", + "服务 ID": "K1e84ad04", + "服务尚未发布": "Ke1e649cb", + "跳转至详情页": "K2e683a7d", + "暂无服务描述": "Ka4b45550", "申请原因": "K1ab0ae5b", "审核意见": "K53c00c3c", "暂无(0)权限,请联系管理员分配。": "Kfd50704d", @@ -114,6 +118,7 @@ "无需审核:允许任何消费者调用该服务": "K1fc2cc28", "人工审核:仅允许通过人工审核的消费者调用该服务": "K8dabb98e", "开启:AI Agent 等产品能够通过 MCP 方式调用服务": "Ke959f135", + "总览": "Kaf9e8011", "永久": "Kbfe02d7f", "否": "K1e9c479e", "是": "Kaddfcb6b", @@ -346,13 +351,12 @@ "查询": "Kee8ae330", "请输入 APIURL 搜索": "Kf8187c33", "服务": "Kb58e0c3f", - "说明文档": "K6cd677b", + "使用说明": "Kdefa9caa", "最近一次更新者": "K617f34f1", "最近一次更新时间": "K6ebca204", "保存": "Kabfe9512", "API 路由": "K51d1eb5d", "API 文档": "Ka2b6d281", - "使用说明": "Kdefa9caa", "服务策略": "K52f72551", "发布": "K36856e71", "订阅管理": "K6382bbfd", @@ -361,7 +365,6 @@ "管理": "K5974bf24", "调用拓扑图": "K3fa5c4c3", "设置": "Kb5c7b82d", - "服务 ID": "K1e84ad04", "新增订阅方": "K39ab0358", "手动添加": "K18307d56", "订阅申请": "K705fe9f5", @@ -559,6 +562,9 @@ "MCP 配置": "K6e9c928f", "Open API 文档": "Kb6d0eb39", "AI 代理集成": "Ke6908f16", + "请先订阅该服务": "K71ed51fa", + "申请": "K4aa9ed2c", + "选择 API Key": "K1bec8cbe", "新增 API Key": "Kb0e0aeda", "API 密钥可用于调用系统级 Open API 和 MCP。": "K9d81999c", "MCP 服务": "Kf106bc62", @@ -660,6 +666,25 @@ "系统级别角色": "K138facd3", "添加角色": "K6eac768d", "团队级别角色": "Kb9c2cf02", + "API / Tools": "K9d526cac", + "消费者": "K7acfcfad", + "HTTP 状态": "Kc68ba0f4", + "IP": "Kb09b747", + "通过系统级别的 API Key 来调用": "K2eacb44f", + "日志详情": "K764bca7c", + "订阅数量": "Ke04bc00d", + "已开启": "K1b97ae0a", + "开启 MCP": "K19ec733b", + "API 使用排名": "Kbee2340", + "消费者使用排名": "Kf6af1f40", + "请求数": "K318a7519", + "Token": "K9ef68e3f", + "平均 Token/s 统计": "K6c016898", + "平均请求数": "K652843b0", + "平均 Token/订阅者统计": "Kf5eeb9c5", + "流量": "K53eb7414", + "平均响应时间": "K7c8d5c23", + "平均流量": "K8158a6e4", "单位:ms,最小值:1": "K2a16c93b", "API 路由设置": "Ka945cfb1", "API 基础信息": "K2e050340", @@ -742,7 +767,6 @@ "退出全屏": "Kaf70c3b", "(0)调用详情": "Kd22841a4", "消费者调用统计": "K61cca533", - "消费者": "K7acfcfad", "请选择消费者": "Kdfff59d4", "调用趋势": "K8c7f2d2e", "(0)-(1)调用趋势": "K657c3452", @@ -783,14 +807,13 @@ "配置集群信息": "Ke5ed9810", "监控设置": "K1a132228", "配置监控信息": "K6af08c3c", - "监控总览": "K4a1a14", - "服务被调用统计": "K69741ea7", - "API 调用统计": "K9c8d9933", + "加载数据失败,请重试": "K6c2d93b6", "亿": "K145e4941", "万": "Ke6a935d", "搜索分类或标签": "Kd59290a2", "暂无API数据": "K6b75bdbc", "搜索或选择消费者": "Kb684c806", + "该消费者已订阅": "K5611e01e", "申请理由": "K4b15d6f5", "支持把当前服务对接主流的 AI Agent平台,实现在 Agent 平台上快速、安全和合规地使用企业开放的 API 能力。": "K2ec0fa56", "可按以下步骤进行对接:": "K35f23b64", @@ -850,8 +873,6 @@ "版本": "K81634069", "更新时间": "Keefda53d", "介绍": "K59cdbec3", - "暂无服务描述": "Ka4b45550", - "申请": "K4aa9ed2c", "无标签": "K96a2f1c8", "分类": "Kb32f0afe", "服务市场": "K370a3eb2", diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 53f4af02..936e9eb5 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -927,5 +927,34 @@ "K71ed51fa": "Please subscribe to the service first", "K1bec8cbe": "Select API Key", "K5611e01e": "This consumer is already subscribed", - "Kaf9e8011": "Overview" + "Kaf9e8011": "Overview", + "Ke1e649cb": "Service not released", + "K2e683a7d": "Open in Portal", + "Ke04bc00d": "Subscribers", + "K1b97ae0a": "Enabled", + "K19ec733b": "Enable MCP", + "Kbee2340": "Top API", + "Kf6af1f40": "Top Consumer", + "K318a7519": "Requests", + "K9ef68e3f": "Token", + "Kfb14ccb0": "Models", + "K10a8bee3": "Avg Token per Second", + "K2727b76b": "Avg Requests per Subscriber", + "K4c7a6704": "Avg Token per Subscriber", + "K53eb7414": "Traffic", + "K7c8d5c23": "Avg Response Time", + "Kf9eb702": "QRS", + "K7f0aa740": "Avg Traffic per Subscriber", + "K9d526cac": "API / Tools", + "Kc68ba0f4": "HTTP Status", + "Kb09b747": "IP", + "K2eacb44f": "Request the API using a system-level API Key", + "K764bca7c": "Log Detail", + "K6c016898": "Avg Token per Second", + "K652843b0": "Avg Requests per Subscriber", + "Kdbf831a0": "Avg Token per Subscriber", + "K8158a6e4": "Avg Traffic per Subscriber", + "K6b882d4a": "Avg Token per Subscriber", + "K6c2d93b6": "Failed to load data, please try again", + "Kf5eeb9c5": "Avg Token per Subscriber" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index 73d1938d..dff7d694 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -949,5 +949,34 @@ "K71ed51fa": "このサービスに先にサブスクリプションしてください", "K1bec8cbe": "APIキーを選択してください", "K5611e01e": "この消費者はすでに購読しています", - "Kaf9e8011": "概要" + "Kaf9e8011": "概要", + "Ke1e649cb": "サービスはまだ公開されていません", + "K2e683a7d": "詳細ページへ移動", + "Ke04bc00d": "サブスクリプション数", + "K1b97ae0a": "有効", + "K19ec733b": "MCP を有効にする", + "Kbee2340": "API 使用ランキング", + "Kf6af1f40": "コンシューマー使用ランキング", + "K318a7519": "リクエスト数", + "K9ef68e3f": "トークン", + "Kfb14ccb0": "モデル使用量", + "K10a8bee3": "平均トークン消費量", + "K2727b76b": "ユーザーあたり平均リクエスト数", + "K4c7a6704": "ユーザーあたり平均トークン消費量", + "K53eb7414": "トラフィック", + "K7c8d5c23": "平均応答時間", + "Kf9eb702": "毎秒リクエスト数", + "K7f0aa740": "ユーザーあたり平均トラフィック", + "K9d526cac": "API / ツール", + "Kc68ba0f4": "HTTP ステータス", + "Kb09b747": "IP", + "K2eacb44f": "システムレベルの API Key で呼び出し", + "K764bca7c": "ログ詳細", + "K6c016898": "平均トークン/s 統計", + "K652843b0": "平均リクエスト数", + "Kdbf831a0": "平均トークン/加入者 統計", + "K8158a6e4": "平均トラフィック", + "K6b882d4a": "平均トークン/加入者", + "K6c2d93b6": "データの読み込みに失敗しました。もう一度お試しください", + "Kf5eeb9c5": "平均トークン/加入者 統計" } diff --git a/frontend/packages/common/src/locales/scan/newJson/zh-CN.json b/frontend/packages/common/src/locales/scan/newJson/zh-CN.json index c5c43eda..9e26dfee 100644 --- a/frontend/packages/common/src/locales/scan/newJson/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/newJson/zh-CN.json @@ -1,69 +1 @@ -{ - "K630c9e6d": "APIPark", - "Ka3e9f580": "发布名称", - "Kb2480682": "策略列表", - "K76036e25": "HTTP 请求头", - "K44607e3f": "全等匹配", - "Kc287500a": "前缀匹配", - "Kfc0b1147": "后缀匹配", - "Ka4a92043": "子串匹配", - "K30b2e44f": "非等匹配", - "Kb1587991": "空值匹配", - "K1e97dbd8": "存在匹配", - "Kc8ee3e62": "不存在匹配", - "K87c5a801": "区分大小写的正则匹配", - "K95f062f1": "不区分大小写的正则匹配", - "Kfbd230a5": "任意匹配", - "Kd85208a3": "驳回", - "Kad6aa439": "已订阅", - "K9a68443b": "取消申请", - "Kaeba0229": "透传客户端请求 Host", - "K6d7e2fd0": "使用上游服务 Host", - "K31332633": "重写 Host", - "K2c2bc64f": "动态服务发现", - "K78b1ca25": "地址", - "K1644b775": "新增", - "Kec91f0db": "申请方消费者", - "K118d8d74": "数据格式", - "Kfe7c7d2d": "关键字", - "K2f57a694": "正则表达式", - "K8953e0a6": "手机号", - "K6f86a038": "身份证号", - "K7954e7c8": "银行卡号", - "K320fdb17": "金额", - "K7867acda": "日期", - "K7d327ae8": "局部显示", - "Kfbf38e3c": "局部遮蔽", - "Kd8c1fbb0": "截取", - "K89829921": "替换", - "K480a7165": "乱序", - "Kea0d69df": "随机字符串", - "Ke7c84d1d": "自定义字符串", - "K49731763": "请输入IP地址或CIDR范围,每条以换行分割", - "K3a34d49b": "待更新", - "Kd2850420": "待删除", - "K83237c89": "输入的IP或CIDR不符合格式", - "K5ae2c87a": "请正确输入路径,如/usr/*或*/usr/*", - "K67f4e9bb": "与外部平台集成时,获取 API 市场中文档信息的域名", - "Kc82b8374": "编辑策略", - "K4b34a5e5": "策略类型", - "K57f0fee8": "匹配条件", - "K10650c58": "数据脱敏规则", - "K1b34a9ab": "配置脱敏规则", - "K26d22405": "匹配值", - "K1546e1fe": "脱敏类型", - "K9b9b0629": "起始位置", - "K52c84fe1": "长度", - "Kde84409c": "替换类型", - "K338653b4": "替换值", - "Kbaeed3b7": "JSON Path", - "K4cd91d61": "脱敏规则", - "K8dcad979": "自定义字符串; 值:", - "K82e3f7b7": "起始位置:(0)位;长度:(1)位", - "K49dfc123": "已选择(0)项(1)数据", - "K8457ea34": "所有(0)", - "K7ca9a795": "属性名称", - "Kc4391744": "属性值", - "K678e13fc": "配置(0)", - "Kf5fd27ed": "输入名称查找用户" -} \ No newline at end of file +{} \ No newline at end of file diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index 789ab22b..99343db3 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -880,5 +880,32 @@ "K71ed51fa": "请先订阅该服务", "K1bec8cbe": "选择 API Key", "K5611e01e": "该消费者已订阅", - "Kaf9e8011": "总览" + "Kaf9e8011": "总览", + "Ke1e649cb": "服务尚未发布", + "K2e683a7d": "跳转至详情页", + "Ke04bc00d": "订阅数量", + "K1b97ae0a": "已开启", + "K19ec733b": "开启 MCP", + "Kbee2340": "API 使用排名", + "Kf6af1f40": "消费者使用排名", + "K318a7519": "请求数", + "K9ef68e3f": "Token", + "Kfb14ccb0": "模型使用量", + "K10a8bee3": "平均 Token 消耗", + "K2727b76b": "人均请求数", + "K4c7a6704": "人均 Token 消耗", + "K53eb7414": "流量", + "K7c8d5c23": "平均响应时间", + "Kf9eb702": "每秒请求数量", + "K7f0aa740": "人均流量", + "K9d526cac": "API / Tools", + "Kc68ba0f4": "HTTP 状态", + "Kb09b747": "IP", + "K2eacb44f": "通过系统级别的 API Key 来调用", + "K764bca7c": "日志详情", + "K6c016898": "平均 Token/s 统计", + "K652843b0": "平均请求数", + "K8158a6e4": "平均流量", + "K6c2d93b6": "加载数据失败,请重试", + "Kf5eeb9c5": "平均 Token/订阅者统计" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index becfa911..9afebe7d 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -949,5 +949,34 @@ "K71ed51fa": "請先訂閱該服務", "K1bec8cbe": "選擇 API Key", "K5611e01e": "該消費者已訂閱", - "Kaf9e8011": "總覽" + "Kaf9e8011": "總覽", + "Ke1e649cb": "服務尚未發布", + "K2e683a7d": "跳轉至詳情頁", + "Ke04bc00d": "訂閱數量", + "K1b97ae0a": "已開啟", + "K19ec733b": "開啟 MCP", + "Kbee2340": "API 使用排名", + "Kf6af1f40": "消費者使用排名", + "K318a7519": "請求數", + "K9ef68e3f": "Token", + "Kfb14ccb0": "模型使用量", + "K10a8bee3": "平均 Token 消耗", + "K2727b76b": "人均請求數", + "K4c7a6704": "人均 Token 消耗", + "K53eb7414": "流量", + "K7c8d5c23": "平均回應時間", + "Kf9eb702": "每秒請求數量", + "K7f0aa740": "人均流量", + "K9d526cac": "API / 工具", + "Kc68ba0f4": "HTTP 狀態", + "Kb09b747": "IP", + "K2eacb44f": "透過系統級 API Key 調用", + "K764bca7c": "日誌詳情", + "K6c016898": "平均 Token/s 統計", + "K652843b0": "平均請求數", + "Kdbf831a0": "每位訂閱者平均 Token 統計", + "K8158a6e4": "平均流量", + "K6b882d4a": "每位訂閱者平均 Token", + "K6c2d93b6": "載入資料失敗,請重試", + "Kf5eeb9c5": "每位訂閱者平均 Token 統計" } diff --git a/frontend/packages/core/src/const/ai-service/type.ts b/frontend/packages/core/src/const/ai-service/type.ts index 82c3825c..401e47a7 100644 --- a/frontend/packages/core/src/const/ai-service/type.ts +++ b/frontend/packages/core/src/const/ai-service/type.ts @@ -15,7 +15,7 @@ export type AiServiceConfigFieldType = { logoFile?:UploadFile; tags?:Array; description?: string; - team?:string; + team?:EntityItem; master?:string; serviceType?:'public'|'inner'; catalogue?:string | string[]; diff --git a/frontend/packages/core/src/const/const.tsx b/frontend/packages/core/src/const/const.tsx index f763da82..f4cf1da3 100644 --- a/frontend/packages/core/src/const/const.tsx +++ b/frontend/packages/core/src/const/const.tsx @@ -96,6 +96,20 @@ export const routerMap: Map = new Map([ key: 'restServiceInside', lazy: lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')), children: [ + { + path: 'overview', + key: 'restServiceInsideOverview', + lazy: lazy( + () => import(/* webpackChunkName: "[request]" */ '@core/pages/serviceOverview/RestServiceContainer') + ) + }, + { + path: 'logs', + key: 'restServiceInsideLogs', + lazy: lazy( + () => import(/* webpackChunkName: "[request]" */ '@core/pages/serviceLogs/RestServiceLogsContainer') + ) + }, { path: 'api', key: 'restServiceInsideApi', @@ -268,6 +282,20 @@ export const routerMap: Map = new Map([ () => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx') ), children: [ + { + path: 'overview', + key: 'aiServiceInsideOverview', + lazy: lazy( + () => import(/* webpackChunkName: "[request]" */ '@core/pages/serviceOverview/AiServiceContainer') + ) + }, + { + path: 'logs', + key: 'aiServiceInsideLogs', + lazy: lazy( + () => import(/* webpackChunkName: "[request]" */ '@core/pages/serviceLogs/AiServiceLogsContainer') + ) + }, { path: 'api', key: 'aiServiceInsideApi', diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index c356ee6a..214a35b6 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -13,6 +13,7 @@ import { } from './type' import { PageProColumns } from '@common/components/aoplatform/PageList' +import { LogItem } from '@core/pages/serviceLogs/ServiceLogs' export enum SubscribeEnum { Rejected = 0, @@ -500,3 +501,124 @@ export const SYSTEM_PUBLISH_ONLINE_COLUMNS = [ } } ] + +/** AI 服务排行 */ +export const AI_SERVICE_TOP_RANKING_LIST: PageProColumns[] = [ + { + title: '名称', + dataIndex: 'name', + ellipsis: true + }, + { + title: '请求总数', + dataIndex: 'request', + ellipsis: true + }, + { + title: 'Token', + dataIndex: 'token', + ellipsis: true + } +] + +/** REST 服务排行 */ +export const REST_SERVICE_TOP_RANKING_LIST: PageProColumns[] = [ + { + title: '名称', + dataIndex: 'name', + ellipsis: true + }, + { + title: '请求总数', + dataIndex: 'request', + ellipsis: true + }, + { + title: '流量', + dataIndex: 'traffic', + ellipsis: true + } +] + +/** REST 服务日志 */ +export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ + { + title: '时间', + dataIndex: 'logTime', + ellipsis: true + }, + { + title: 'API / Tools', + dataIndex: ['api', 'name'], + ellipsis: true + }, + { + title: '消费者', + dataIndex: ['consumers', 'name'], + ellipsis: true + }, + { + title: 'HTTP 状态', + dataIndex: 'status', + ellipsis: true + }, + { + title: 'IP', + dataIndex: 'ip', + ellipsis: true + }, + { + title: '响应时间', + dataIndex: 'responseTime', + ellipsis: true + }, + { + title: '流量', + dataIndex: 'traffic', + ellipsis: true + } +] + +/** AI 服务日志 */ +export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ + { + title: '时间', + dataIndex: 'logTime', + ellipsis: true + }, + { + title: 'API / Tools', + dataIndex: ['api', 'name'], + ellipsis: true + }, + { + title: '消费者', + dataIndex: ['consumers', 'name'], + ellipsis: true + }, + { + title: 'HTTP 状态', + dataIndex: 'status', + ellipsis: true + }, + { + title: '模型', + dataIndex: 'model', + ellipsis: true + }, + { + title: 'IP', + dataIndex: 'ip', + ellipsis: true + }, + { + title: 'Token/s', + dataIndex: 'tokenPerSecond', + ellipsis: true + }, + { + title: 'Token', + dataIndex: 'token', + ellipsis: true + } +] diff --git a/frontend/packages/core/src/index.css b/frontend/packages/core/src/index.css index 9809791a..a18fb3bb 100644 --- a/frontend/packages/core/src/index.css +++ b/frontend/packages/core/src/index.css @@ -1156,6 +1156,22 @@ p{ align-items: center; } +.ranking-list .ant-pro-table{ + overflow: hidden; + border-radius: 10px; + border: none !important; +} +.ranking-list .ant-table-tbody:not(tbody) .ant-table-cell{ + padding: 10px 10px !important; +} +.ranking-list .ant-table-header .ant-table-thead th{ + background-color: #fff !important; + padding: 10px 10px !important; +} +.ranking-list .ant-table-header .ant-table-thead th::before{ + display: none; +} + .ant-alert-info{ background: #1784FC1A !important; } diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx index f42c6b47..0dc81acd 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx @@ -9,7 +9,7 @@ import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx' import { AiServiceConfigFieldType } from '@core/const/ai-service/type.ts' import { App, Menu, MenuProps } from 'antd' import { ItemType, MenuItemGroupType, MenuItemType } from 'antd/es/menu/interface' -import Paragraph from 'antd/es/typography/Paragraph' +import ServiceInfoCard from '@common/components/aoplatform/serviceInfoCard.tsx' import { cloneDeep } from 'lodash-es' import { FC, useEffect, useMemo, useState } from 'react' import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom' @@ -67,6 +67,7 @@ const AiServiceInsidePage: FC = () => { 'assets', null, [ + getItem({$t('总览')}, 'overview', undefined, undefined, undefined, ''), getItem( {$t('API 路由')}, 'route', @@ -149,7 +150,8 @@ const AiServiceInsidePage: FC = () => { 'project.myAiService.topology.view' ) : null, - getItem({$t('设置')}, 'setting', undefined, undefined, undefined, '') + getItem({$t('设置')}, 'setting', undefined, undefined, undefined, ''), + getItem({$t('日志')}, 'logs', undefined, undefined, undefined, '') ], 'group' ) @@ -202,7 +204,7 @@ const AiServiceInsidePage: FC = () => { } else if (serviceId !== currentUrl.split('/')[currentUrl.split('/').length - 1]) { setActiveMenu(currentUrl.split('/')[currentUrl.split('/').length - 1]) } else { - setActiveMenu('route') + setActiveMenu('overview') } }, [currentUrl]) @@ -231,17 +233,8 @@ const AiServiceInsidePage: FC = () => { {showMenu ? ( - {$t('服务 ID')}:{serviceId || '-'} - - ) - } - ]} backUrl="/service/list" + customBanner={} >
{ + return +} + +export default AiServiceLogsContainer \ No newline at end of file diff --git a/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx new file mode 100644 index 00000000..ce758d38 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx @@ -0,0 +1,89 @@ +import { IconButton } from '@common/components/postcat/api/IconButton' +import useCopyToClipboard from '@common/hooks/copy' +import { RESPONSE_TIPS } from '@common/const/const' +import { $t } from '@common/locales/index.ts' +import { App } from 'antd' +import ReactJson from 'react-json-view' + +const ApiNetWorkDataPreview = ({ configContent = {} }: { configContent?: { [key: string]: string | undefined } }) => { + /** 复制组件 */ + const { copyToClipboard } = useCopyToClipboard() + /** 弹窗组件 */ + const { message } = App.useApp() + /** + * 复制 + * @param value + * @returns + */ + const handleCopy = async (value: string): Promise => { + if (value) { + copyToClipboard(value) + message.success($t(RESPONSE_TIPS.copySuccess)) + } + } + /** + * 判断字符串是否是有效的JSON对象字符串 + */ + const isJsonString = (str: string): boolean => { + try { + const parsed = JSON.parse(str) + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) + } catch (e) { + return false + } + } + + return ( + <> + {Object.keys(configContent).map((item) => { + return ( +
+
{item}
+
+ {!configContent[item] ? ( +

+              ) : isJsonString(configContent[item] || '') ? (
+                // 如果是有效的JSON对象字符串,使用ReactJson渲染
+                
+              ) : (
+                // 如果是普通字符串,直接用pre渲染
+                
{configContent[item]}
+ )} + handleCopy(configContent[item] || '')} + sx={{ + position: 'absolute', + top: '5px', + right: '5px', + color: '#999', + transition: 'none', + '&.MuiButtonBase-root:hover': { + background: 'transparent', + color: '#3D46F2', + transition: 'none' + } + }} + > +
+
+ ) + })} + + ) +} +export default ApiNetWorkDataPreview diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx new file mode 100644 index 00000000..5a607f12 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -0,0 +1,424 @@ +import { Descriptions, DescriptionsProps, Spin, Tabs, Tooltip, message } from 'antd' +import { useEffect, useMemo, useState } from 'react' +import { $t } from '@common/locales/index.ts' +import React from 'react' +import { ExclamationCircleOutlined, LoadingOutlined } from '@ant-design/icons' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' +import ApiNetWorkDataPreview from './ApiNetWorkDataPreview' +import { LogItem } from './ServiceLogs' +import { useFetch } from '@common/hooks/http' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' + +// 定义状态码颜色映射枚举 +export enum HttpStatusColor { + SUCCESS = '#7EC26A', + CLIENT_ERROR = '#F2CF59', + SERVER_ERROR = '#f80f34' +} + +type LogDetailProps = { + selectedRow?: LogItem + serviceType: 'aiService' | 'restService' + serviceId?: string + teamId?: string +} + +type AIServiceDetailType = { + id: string + api: { + id: string + name: string + } + logTime: string + consumer: { + id: string + name: string + } + isSystemConsumer: boolean + status: string + provider: { + id: string + name: string + } + model: string + ip: string + request: { + header: string + body: string + origin: string + token: number + } + response: { + header: string + body: string + origin: string + token: string + } +} + +type RestServiceDetailType = { + id: string + api: { + id: string + name: string + } + logTime: string + consumer: { + id: string + name: string + } + isSystemConsumer: boolean + status: string + ip: string + request: { + header: string + origin: string + } + response: { + header: string + origin: string + } +} + +const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailProps) => { + /** 顶部描述 */ + const [descriptionItems, setDescriptionItems] = useState() + /** 全局状态 */ + const { state } = useGlobalContext() + /** Request 标签页数据 */ + const [requestInfoData, setRequestInfoData] = useState<{ [key: string]: string | undefined }>() + /** Response 标签页数据 */ + const [responseInfoData, setResponseInfoData] = useState<{ [key: string]: string | undefined }>() + /** 面板 loading */ + const [dashboardLoading, setDashboardLoading] = useState(true) + /** + * 请求数据 + */ + const { fetchData } = useFetch() + + /** + * 根据状态码返回对应颜色的文本 + * @param status 状态 + * @returns + */ + const renderStatusWithColor = (status: string) => { + // 获取状态码首位数字 + const firstDigit = status.charAt(0) + let color = '' + switch (firstDigit) { + case '2': + color = HttpStatusColor.SUCCESS + break + case '4': + color = HttpStatusColor.CLIENT_ERROR + break + case '5': + color = HttpStatusColor.SERVER_ERROR + break + default: + break + } + return color ? {status} : status + } + + /** + * 获取标签页内容 + */ + const tabItems = useMemo( + () => [ + { + key: 'request', + label: 'Request', + children: + }, + { + key: 'response', + label: 'Response', + children: + } + ], + [state.language, requestInfoData, responseInfoData] + ) + + /** + * 设置 AI 描述文案 + */ + const getAIServiceDescriptionItemsList = ({ + time, + api, + consumer, + status, + model, + ip + }: { + time: string + api: string + consumer: string + status: string + model: string + ip: string + }) => { + setDescriptionItems([ + { + key: 'time', + label: $t('时间'), + children: time + }, + { + key: 'api', + label: $t('API / Tools'), + children: api + }, + { + key: 'consumer', + label: $t('消费者'), + children: consumer + }, + { + key: 'httpStatus', + label: $t('HTTP 状态'), + children: renderStatusWithColor(status) + }, + { + key: 'model', + label: $t('模型'), + children: model + }, + { + key: 'ip', + label: $t('IP'), + children: ip + } + ]) + } + + /** + * 设置 REST 描述文案 + */ + const getRestServiceDescriptionItemsList = ({ + time, + api, + consumer, + isSystemConsumer, + status, + ip + }: { + time: string + api: string + consumer: string + isSystemConsumer?: boolean + status: string + ip: string + }) => { + setDescriptionItems([ + { + key: 'time', + label: $t('时间'), + children: time + }, + { + key: 'api', + label: $t('API / Tools'), + children: api + }, + { + key: 'consumer', + label: $t('消费者'), + children: ( + <> + {consumer} + {isSystemConsumer && ( + + System-level API Key + + + + + + + )} + + ) + }, + { + key: 'httpStatus', + label: $t('HTTP 状态'), + children: renderStatusWithColor(status) + }, + { + key: 'ip', + label: $t('IP'), + children: ip + } + ]) + } + + /** + * 获取 AI 服务日志详情 + */ + const getAIServiceLogDetail = () => { + fetchData>('service/log/ai', { + method: 'GET', + eoParams: { log: selectedRow?.id, service: serviceId, team: teamId }, + eoTransformKeys: ['is_system_consumer', 'log_time'], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + // const result = data.log + const result = { + id: '123', + api: { + id: '222', + name: 'api222' + }, + logTime: '2023-01-01 00:00:00', + consumer: { + id: '333', + name: 'consumers333' + }, + isSystemConsumer: false, + status: '200', + provider: { + id: '444', + name: 'provider444' + }, + model: 'model1', + ip: '1.1.1.1', + request: { + header: + '{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + origin: '123', + token: 0 + }, + response: { + header: + '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + origin: '312', + token: '333' + } + } + getAIServiceDescriptionItemsList({ + time: result.logTime, + api: result.api.name, + consumer: result.consumer.name, + status: result.status, + model: result.model, + ip: result.ip + }) + setRequestInfoData({ + Header: result.request.header, + Body: result.request.body, + Origin: result.request.origin + }) + setResponseInfoData({ + Header: result.response.header, + Body: result.response.body, + Origin: result.response.origin + }) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + setDashboardLoading(false) + }) + } + /** + * 获取 REST 服务日志详情 + */ + const getRestServiceLogDetail = () => { + fetchData>('service/log/rest', { + method: 'GET', + eoParams: { log: selectedRow?.id, service: serviceId, team: teamId }, + eoTransformKeys: ['is_system_consumer', 'log_time'], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const result = { + id: '123', + api: { + id: '222', + name: 'api222' + }, + logTime: '2023-01-01 00:00:00', + consumer: { + id: '333', + name: 'consumers333' + }, + isSystemConsumer: true, + status: '200', + ip: '1.1.1.1', + request: { + header: + '{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + origin: '123' + }, + response: { + header: + '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', + origin: '312' + } + } + getRestServiceDescriptionItemsList({ + time: result.logTime, + api: result.api.name, + consumer: result.consumer.name, + status: result.status, + ip: result.ip, + isSystemConsumer: result.isSystemConsumer + }) + setRequestInfoData({ + Header: result.request.header, + Origin: result.request.origin + }) + setResponseInfoData({ + Header: result.response.header, + Origin: result.response.origin + }) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + setDashboardLoading(false) + }) + } + useEffect(() => { + setDashboardLoading(true) + serviceType === 'aiService' ? getAIServiceLogDetail() : getRestServiceLogDetail() + }, [serviceType]) + + return ( + +
+ +
+
+ } + spinning={dashboardLoading} + > + +
+ +
+ + ) +} + +export default LogDetail diff --git a/frontend/packages/core/src/pages/serviceLogs/RestServiceLogsContainer.tsx b/frontend/packages/core/src/pages/serviceLogs/RestServiceLogsContainer.tsx new file mode 100644 index 00000000..db2ac201 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceLogs/RestServiceLogsContainer.tsx @@ -0,0 +1,7 @@ +import ServiceLogs from "./ServiceLogs" + +const RestServiceLogsContainer = () => { + return +} + +export default RestServiceLogsContainer \ No newline at end of file diff --git a/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx new file mode 100644 index 00000000..0fc440ed --- /dev/null +++ b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx @@ -0,0 +1,282 @@ +import { LoadingOutlined } from '@ant-design/icons' +import { Drawer, Spin, message } from 'antd' +import { useEffect, useMemo, useRef, useState } from 'react' +import DateSelectFilter, { TimeOption } from '../serviceOverview/filter/DateSelectFilter' +import { TimeRange } from '@common/components/aoplatform/TimeRangeSelector' +import PageList from '@common/components/aoplatform/PageList' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' +import { REST_SERVICE_LOG_LIST, AI_SERVICE_LOG_LIST } from '@core/const/system/const' +import { $t } from '@common/locales/index.ts' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { useFetch } from '@common/hooks/http' +import LogDetail, { HttpStatusColor } from './LogDetail' +import { useParams } from 'react-router-dom' +import { ActionType } from '@ant-design/pro-components' +import { getTime } from '@dashboard/utils/dashboard' + +export type LogItem = { + id: string + api: { + id: string + name: string + } + status: number + logTime: string + responseTime: string + token?: number + model?: string + tokenPerSecond?: string + traffic?: string + consumers?: { + id: string + name: string + } + provider?: { + id: string + name: string + } +} + +const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' }) => { + /** 路由参数 */ + const { serviceId, teamId } = useParams<{ serviceId: string; teamId: string }>() + /** 面板 loading */ + const [dashboardLoading, setDashboardLoading] = useState(true) + /** 当前选中的时间范围 */ + const [timeRange, setTimeRange] = useState() + /** 默认时间 */ + const [defaultTime] = useState('sevenDays') + /** 全局状态 */ + const { state } = useGlobalContext() + /** + * 请求数据 + */ + const { fetchData } = useFetch() + // 打开侧边弹窗 + const [drawerOpen, setDrawerOpen] = useState(false) + /** 选中的行 */ + const [selectedRow, setSelectedRow] = useState() + /** + * 列表ref + */ + const pageListRef = useRef(null) + /** 列 */ + const columns = useMemo(() => { + return [...(serviceType === 'aiService' ? AI_SERVICE_LOG_LIST : REST_SERVICE_LOG_LIST)].map((x) => { + if (x.dataIndex === 'status') { + x.render = (text: any, record: any) => ( + <> +
+ {renderStatusWithColor(record.status)} +
+ + ) + } + return { + ...x, + title: typeof x.title === 'string' ? $t(x.title as string) : x.title + } + }) + }, [state.language]) + + /** + * 根据状态码返回对应颜色的文本 + * @param status 状态 + * @returns + */ + const renderStatusWithColor = (status: string | number) => { + // 获取状态码首位数字 + const firstDigit = status.toString().charAt(0) + let color = '' + switch (firstDigit) { + case '2': + color = HttpStatusColor.SUCCESS + break + case '4': + color = HttpStatusColor.CLIENT_ERROR + break + case '5': + color = HttpStatusColor.SERVER_ERROR + break + default: + break + } + return color ? {status} : status + } + + /** + * 获取 AI 列表数据 + * @param dataType + * @returns + */ + const getAiServiceLogList = () => { + return fetchData>(`service/logs/ai`, { + method: 'GET', + eoParams: { + service: serviceId, + team: teamId, + start: timeRange?.start, + end: timeRange?.end + }, + eoTransformKeys: ['log_time', 'response_time', 'token_per_second'], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }) + .then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + // 保存数据 + return { + data: [ + { + id: '123123', + api: { + id: '444', + name: 'api1' + }, + ip: '127.0.0.1', + status: 200, + logTime: '2023-01-01 00:00:00', + token: 123, + consumers: { + id: '333', + name: 'consumers333' + }, + model: 'GPT444', + tokenPerSecond: '123m/s' + } + ], + total: 1, + success: true + } + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + return { data: [], success: false } + } + }) + .catch(() => { + return { data: [], success: false } + }) + } + /** + * 获取 REST 列表数据 + * @param dataType + * @returns + */ + const getRestServiceLogList = () => { + return fetchData>(`service/logs/rest`, { + method: 'GET', + eoParams: { + service: serviceId, + team: teamId, + start: timeRange?.start, + end: timeRange?.end + }, + eoTransformKeys: ['log_time', 'response_time', 'token_per_second'], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }) + .then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const data = [] + for (let i = 0; i < 100; i++) { + data.push({ + id: '123123' + i, + api: { + id: '444' + i, + name: 'api1' + i + }, + ip: '127.0.0.1', + status: 200, + logTime: '2023-01-01 00:00:00', + responseTime: '1111-01-01 00:00:00', + traffic: '123', + consumers: { + id: '123' + i, + name: 'consumers222' + i + } + }) + } + // 保存数据 + return { + data: data, + total: data.length, + success: true + } + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + return { data: [], success: false } + } + }) + .catch(() => { + return { data: [], success: false } + }) + } + + useEffect(() => { + const { startTime, endTime } = getTime(defaultTime, []) + setTimeRange({ + start: startTime, + end: endTime + }) + }, []) + useEffect(() => { + if (timeRange) { + pageListRef.current?.reload() + } + }, [timeRange]) + + /** 行点击 */ + const handleRowClick = (record: LogItem) => { + setSelectedRow(record) + setDrawerOpen(true) + } + + /** 时间选择回调 */ + const selectCallback = (date: TimeRange) => { + setTimeRange(date) + } + + useEffect(() => { + setDashboardLoading(false) + }, []) + return ( + +
+ +
+
+ } + spinning={dashboardLoading} + > +
+ +
+ (serviceType === 'aiService' ? getAiServiceLogList() : getRestServiceLogList())} + onRowClick={(row: LogItem) => handleRowClick(row)} + /> +
+ setDrawerOpen(false)} + open={drawerOpen} + > + + +
+ + ) +} + +export default ServiceLogs diff --git a/frontend/packages/core/src/pages/serviceOverview/AiServiceContainer.tsx b/frontend/packages/core/src/pages/serviceOverview/AiServiceContainer.tsx new file mode 100644 index 00000000..501fb783 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/AiServiceContainer.tsx @@ -0,0 +1,7 @@ +import ServiceOverview from "./serviceOverview" + +const AiServiceContainer = () => { + return +} + +export default AiServiceContainer \ No newline at end of file diff --git a/frontend/packages/core/src/pages/serviceOverview/RestServiceContainer.tsx b/frontend/packages/core/src/pages/serviceOverview/RestServiceContainer.tsx new file mode 100644 index 00000000..2f527421 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/RestServiceContainer.tsx @@ -0,0 +1,12 @@ +import { FC } from 'react' +import ServiceOverview from './serviceOverview' + +const RestServiceContainer: FC = () => { + return ( + <> + + + ) +} + +export default RestServiceContainer diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx new file mode 100644 index 00000000..200c985d --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -0,0 +1,121 @@ +import { useEffect, useRef, useState } from 'react' +import ECharts, { EChartsOption } from 'echarts-for-react' +import { $t } from '@common/locales' + +type AreaChartInfo = { + title: string + value: string + date: string[] + data: number[] +} +type ServiceAreaCharProps = { + customClassNames?: string + dataInfo?: AreaChartInfo + height?: number +} + +const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCharProps) => { + const chartRef = useRef(null) + const [option, setOption] = useState({}) + const setChartOption = (dataInfo: AreaChartInfo) => { + const option = { + tooltip: { + trigger: 'axis', + position: function (pt) { + return [pt[0], '10%'] + } + }, + title: { + show: false + }, + toolbox: { + show: false + }, + grid: { + left: '5%', + right: '3%', + bottom: '5%', + top: '100px', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: dataInfo.date + }, + yAxis: { + type: 'value', + boundaryGap: [0, '5%'], + axisLine: { + show: false + }, + axisTick: { + show: false + }, + axisLabel: { + show: false + } + }, + dataZoom: [], + series: [ + { + name: dataInfo.title, + type: 'line', + symbol: 'none', + sampling: 'lttb', + itemStyle: { + color: 'rgb(255, 70, 131)' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgb(255, 158, 68)' + }, + { + offset: 1, + color: 'rgb(255, 70, 131)' + } + ] + } + }, + data: dataInfo.data + } + ] + } + setOption(option) + } + useEffect(() => { + if (!dataInfo) return + setChartOption(dataInfo) + }, [dataInfo]) + return ( +
+
+
{$t(dataInfo?.title || '')}
+
+ {dataInfo?.value} +
+
+ + 381 T/s +
+
+ + 381 T/s +
+
+
+
+ +
+ ) +} + +export default ServiceAreaChart diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx new file mode 100644 index 00000000..7108aea2 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -0,0 +1,174 @@ +import ECharts, { EChartsOption } from 'echarts-for-react' +import { useEffect, useRef, useState } from 'react' +import { $t } from '@common/locales/index.ts' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' + +export type BarChartInfo = { + title: string + value: string + date: string[] + data: { + name: string + color: string + value: number[] + }[] +} + +type ServiceBarCharProps = { + customClassNames?: string + dataInfo?: BarChartInfo + height?: number +} + +const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => { + const chartRef = useRef(null) + const [option, setOption] = useState({}) + const [detaultColor] = useState('#5470c6') + const setChartOption = (dataInfo: BarChartInfo) => { + const isNumberArray = typeof dataInfo.data[0] !== 'object' + const legendData = isNumberArray ? [dataInfo.title] : dataInfo.data.map((item) => item.name) + const tooltipFormatter = (params: { name: string; color: string; seriesIndex?: number }) => { + let tooltipContent = `
+
${isNumberArray ? '' : params.name}
` + const data = isNumberArray + ? [ + { + name: params.name, + color: detaultColor, + value: dataInfo.data + } + ] + : dataInfo.data + // 为每个数据系列添加一行 + data.forEach((item, index) => { + const color = item.color + const name = item.name + const value = item.value[dataInfo.date.indexOf(params.name)] || 0 + + const marker = `` + tooltipContent += `
+ ${marker} ${name} ${value} +
` + }) + + tooltipContent += '
' + return tooltipContent + } + const option: EChartsOption = { + title: [ + { + text: '{titleStyle|' + $t(dataInfo.title) + '}\n{valueStyle|' + dataInfo.value + '}', + left: '4%', + top: '0', + textStyle: { + rich: { + titleStyle: { + fontSize: 14, + color: '#999', + fontWeight: 'normal', + lineHeight: 20 + }, + valueStyle: { + fontSize: 25, + color: '#000', + fontWeight: 500, + lineHeight: 40 + } + } + } + } + ], + grid: { + left: '5%', + right: '3%', + bottom: '5%', + top: '100px', + containLabel: true + }, + tooltip: { + trigger: 'item', + formatter: function (params: { name: string; color: string; seriesIndex?: number }) { + return tooltipFormatter(params) + } + }, + legend: { + show: false, + data: legendData, + right: '10px', + top: '30px', + itemWidth: 10, + itemHeight: 10, + textStyle: { + color: '#333' + }, + icon: 'rect' + }, + xAxis: { + type: 'category', + data: dataInfo.date, + axisTick: { + show: false + }, + axisLine: { + lineStyle: { + color: '#ccc' + } + } + }, + yAxis: { + type: 'value', + name: '', + min: 0, + splitLine: { + lineStyle: { + type: 'dashed', + color: '#eee' + } + }, + axisLabel: { + formatter: '{value}' + } + }, + series: isNumberArray + ? [ + { + name: dataInfo.title, + type: 'bar', + stack: '总量', + emphasis: { + focus: 'series' + }, + itemStyle: { + color: detaultColor + }, + data: dataInfo.data + } + ] + : dataInfo.data.map((item) => ({ + name: item.name, + type: 'bar', + stack: '总量', + emphasis: { + focus: 'series' + }, + itemStyle: { + color: item.color + }, + data: item.value + })) + } + setOption(option) + } + + useEffect(() => { + if (!dataInfo) return + setChartOption(dataInfo) + }, [dataInfo]) + return ( +
+ +
+ ) +} + +export default ServiceBarChar diff --git a/frontend/packages/core/src/pages/serviceOverview/filter/DateSelectFilter.tsx b/frontend/packages/core/src/pages/serviceOverview/filter/DateSelectFilter.tsx new file mode 100644 index 00000000..2a59b9a2 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/filter/DateSelectFilter.tsx @@ -0,0 +1,37 @@ +import TimeRangeSelector, { RangeValue, TimeRange } from '@common/components/aoplatform/TimeRangeSelector' +import { useState } from 'react' + +export type TimeOption = '' | 'hour' | 'day' | 'threeDays' | 'sevenDays' +const DateSelectFilter = ({ + selectCallback, + defaultTime, + customClassNames +}: { + selectCallback: (timeRange: TimeRange) => void + defaultTime: TimeOption + customClassNames?: string +}) => { + /** 默认时间 */ + const [timeButton, setTimeButton] = useState(defaultTime || 'hour') + /** 日期选择 */ + const [datePickerValue, setDatePickerValue] = useState() + /** 时间范围变化 */ + const handleTimeRangeChange = (timeRange: TimeRange) => { + selectCallback(timeRange) + } + + return ( +
+ +
+ ) +} + +export default DateSelectFilter diff --git a/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx new file mode 100644 index 00000000..836c7b2b --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx @@ -0,0 +1,86 @@ +import { Button, Card } from 'antd' +import { useEffect, useState } from 'react' +import { $t } from '@common/locales' +import { useNavigate } from 'react-router-dom' +import { Icon } from '@iconify/react/dist/iconify.js' + +/** 服务指标 */ +type IndicatorType = { + title: string + link?: string + content: string | React.ReactNode +} +const Indicator = ({ indicatorInfo }: { indicatorInfo: any }) => { + /** 服务指标 */ + const [indicatorList, setIndicator] = useState([]) + /** 路由跳转 */ + const navigateTo = useNavigate() + + /** 设置服务指标 */ + const setIndicatorList = () => { + setIndicator([ + { + title: indicatorInfo?.enableMcp ? 'APIs / Tools' : 'APIs', + link: `/serviceHub/detail/${indicatorInfo?.serviceId}`, + content: indicatorInfo?.apiNum ?? 0 + }, + { + title: $t('订阅数量'), + link: `/consumer/list/${indicatorInfo?.teamId}`, + content: indicatorInfo?.subscriberNum ?? 0 + }, + { + title: 'MCP', + content: ( + <> + {/* green */} + + + ) + } + ]) + } + + useEffect(() => { + if (!indicatorInfo) return + setIndicatorList() + }, [indicatorInfo]) + + return ( +
+ {indicatorList.map((item, index) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px]' + }} + onClick={() => { + window.open(item.link) + }} + > +
+ {item.title} + {item.link && } +
+
{item.content}
+
+ ))} +
+ ) +} + +export default Indicator diff --git a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx new file mode 100644 index 00000000..97717a2c --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx @@ -0,0 +1,91 @@ +import { useMemo, useRef, useEffect } from 'react' +import PageList from '@common/components/aoplatform/PageList' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' +import { $t } from '@common/locales/index.ts' +import { Card } from 'antd' +import { AI_SERVICE_TOP_RANKING_LIST, REST_SERVICE_TOP_RANKING_LIST } from '@core/const/system/const' + +interface RankingListData { + [key: string]: Array<{ + id: string; + name: string; + request: number; + token?: number; + traffic?: number; + }>; +} + +interface PageListRef { + reload: () => void; + [key: string]: any; +} + +const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingListData; serviceType: 'aiService' | 'restService' }) => { + /** 全局状态 */ + const { state } = useGlobalContext() + /** 表格 ref */ + const tableRefs = useRef<{ [key: string]: PageListRef | null }>({}); + /** 列 */ + const columns = useMemo(() => { + return [...(serviceType === 'aiService' ? AI_SERVICE_TOP_RANKING_LIST : REST_SERVICE_TOP_RANKING_LIST)].map((x) => { + return { + ...x, + title: typeof x.title === 'string' ? $t(x.title as string) : x.title + } + }) + }, [serviceType, state.language]) + + /** 监听 serviceType 变化,刷新所有表格 */ + useEffect(() => { + // 重新加载所有表格数据 + if (Object.keys(tableRefs.current).length > 0) { + Object.values(tableRefs.current).forEach(ref => { + // 如果组件实例存在并且有reload方法 + if (ref && typeof ref.reload === 'function') { + ref.reload(); + } + }); + } + }, [serviceType, topRankingList]) + + /** + * 获取表格数据 + * @param item + * @returns + */ + const getTableData = (item: string) => { + return new Promise((resolve, reject) => { + resolve({ data: topRankingList[item], success: true }) + }) + } + return ( +
+ {Object.keys(topRankingList)?.map((item: any, index: number) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px] pb-[0px]' + }} + > +
+ {item === 'TOP API' ? $t('API 使用排名') : $t('消费者使用排名')} +
+ getTableData(item)} + showPagination={false} + tableClass="ranking-list" + ref={ref => { + if (ref) tableRefs.current[item] = ref; + }} + /> +
+ ))} +
+ ) +} + +export default RankingList diff --git a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx new file mode 100644 index 00000000..8ba0a917 --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx @@ -0,0 +1,541 @@ +import { Card, Spin } from 'antd' +import { useEffect, useState } from 'react' +import { useParams } from 'react-router-dom' +import { $t } from '@common/locales/index.ts' +import Indicator from './indicator/Indicator' +import { LoadingOutlined } from '@ant-design/icons' +import DateSelectFilter, { TimeOption } from './filter/DateSelectFilter' +import { TimeRange } from '@common/components/aoplatform/TimeRangeSelector' +import ServiceBarChar, { BarChartInfo } from './charts/ServiceBarChar' +import { useFetch } from '@common/hooks/http' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { App } from 'antd' +import ServiceAreaChart from './charts/ServiceAreaChart' +import RankingList from './rankingList/RankingList' +import { getTime } from '@dashboard/utils/dashboard' +import { setBarChartInfoData } from './utils' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' + +const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restService' }) => { + /** 路由参数 */ + const { serviceId, teamId } = useParams<{ serviceId: string; teamId: string }>() + /** 面板 loading */ + const [dashboardLoading, setDashboardLoading] = useState(true) + /** 默认时间 */ + const [defaultTime] = useState('sevenDays') + /** 当前选中的时间范围 */ + const [timeRange, setTimeRange] = useState() + /** 总数数据 */ + const [barChartInfo, setBarChartInfo] = useState() + /** 平均值数据 */ + const [perBarChartInfo, setPerBarChartInfo] = useState() + /** 指标数据 */ + const [indicatorInfo, setIndicatorInfo] = useState([]) + /** 排名表格数据 */ + const [topRankingList, setTopRankingList] = useState([]) + /** 获取服务信息 */ + const { fetchData } = useFetch() + /** 弹窗组件 */ + const { message } = App.useApp() + /** 全局状态 */ + const { state } = useGlobalContext() + /** AI 服务数据 */ + const [aiServiceOverview, setAiServiceOverview] = useState() + /** REST 服务数据 */ + const [restServiceOverview, setRestServiceOverview] = useState() + /** 时间选择回调 */ + const selectCallback = (date: TimeRange) => { + setTimeRange(date) + } + + /** 获取 AI 服务信息 */ + const getAIServiceOverview = () => { + fetchData>('service/overview/monitor/ai', { + method: 'GET', + eoParams: { service: serviceId, team: teamId, start: timeRange?.start, end: timeRange?.end }, + eoTransformKeys: [ + 'enable_mcp', + 'subscriber_num', + 'api_num', + 'service_kind', + 'avaliable_monitor', + 'request_overview', + 'token_overview', + 'avg_token_overview', + 'avg_request_per_subscriber_overview', + 'avg_token_per_subscriber_overview', + 'request_total', + 'token_total', + 'avg_token', + 'avg_request_per_subscriber', + 'avg_token_per_subscriber' + ], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + enableMcp: true, + subscriberNum: 11, + apiNum: 3, + serviceKind: 'ai', + avaliableMonitor: false, + requestOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0, + fsdf: 4.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0, + fsdf: 5.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0, + fsdf: 6.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0, + fsdf: 7.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0, + fsdf: 8.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0, + fsdf: 9.0 + } + ], + tokenOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0 + } + ], + avgTokenOverview: [11, 231, 343, 1414, 25, 362], + avgRequestPerSubscriberOverview: [1, 2, 3, 4, 5, 6], + avgTokenPerSubscriberOverview: [4, 5, 1, 11, 4, 9], + requestTotal: '12 GB', + tokenTotal: '14 GB', + avgToken: '1 k', + avgRequestPerSubscriber: '2 k', + avgTokenPerSubscriber: '3 k', + date: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + } + // 存储 AI 服务数据 + setAiServiceOverview(serviceOverview) + // 设置 AI 报表数据 + setAiChartInfoData(serviceOverview) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + setDashboardLoading(false) + }) + } + + /** + * 设置 REST 服务数据 + * */ + const setRestChartInfoData = (serviceOverview: any) => { + // 设置指标数据 + setIndicatorInfo({ + apiNum: serviceOverview.apiNum, + subscriberNum: serviceOverview.subscriberNum, + teamId: teamId, + enableMcp: serviceOverview.enableMcp, + serviceId: serviceId + }) + // 设置总数数据 + setBarChartInfo([ + // 服务请求次数 + setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + // 流量消耗总数 + setBarChartInfoData({ + title: $t('流量'), + data: serviceOverview.trafficOverview, + value: serviceOverview.trafficTotal, + date: serviceOverview.date + }) + ]) + // 设置平均值数据 + setPerBarChartInfo([ + // 各个模型使用量 + { + title: $t('平均响应时间'), + data: serviceOverview.avgResponseTimeOverview, + value: serviceOverview.avgResponseTime, + date: serviceOverview.date, + type: 'area' + }, + // 平均请求 + setBarChartInfoData({ + title: $t('平均请求数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + value: serviceOverview.avgRequestPerSubscriber, + date: serviceOverview.date + }), + // 平均流量消耗 + setBarChartInfoData({ + title: $t('平均流量'), + data: serviceOverview.avgTrafficPerSubscriberOverview, + value: serviceOverview.avgTrafficPerSubscriber, + date: serviceOverview.date + }) + ]) + } + + /** + * 设置 AI 服务数据 + * */ + const setAiChartInfoData = (serviceOverview: any) => { + // 设置指标数据 + setIndicatorInfo({ + apiNum: serviceOverview.apiNum, + subscriberNum: serviceOverview.subscriberNum, + teamId: teamId, + enableMcp: serviceOverview.enableMcp, + serviceId: serviceId + }) + // 设置总数数据 + setBarChartInfo([ + // 服务请求次数 + setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + // token 消耗总数 + setBarChartInfoData({ + title: $t('Token'), + data: serviceOverview.tokenOverview, + value: serviceOverview.tokenTotal, + date: serviceOverview.date + }) + ]) + // 设置平均值数据 + setPerBarChartInfo([ + // 平均 token 消耗 + { + title: $t('平均 Token/s 统计'), + data: serviceOverview.avgTokenOverview, + value: serviceOverview.avgToken, + date: serviceOverview.date, + type: 'area' + }, + // 平均请求 + setBarChartInfoData({ + title: $t('平均请求数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + value: serviceOverview.avgRequestPerSubscriber, + date: serviceOverview.date + }), + // 评价 token 消耗 + setBarChartInfoData({ + title: $t('平均 Token/订阅者统计'), + data: serviceOverview.avgTokenPerSubscriberOverview, + value: serviceOverview.avgTokenPerSubscriber, + date: serviceOverview.date + }) + ]) + } + + /** 获取 REST 服务信息 */ + const getRestServiceOverview = () => { + fetchData>('service/overview/monitor/rest', { + method: 'GET', + eoParams: { service: serviceId, team: teamId, start: timeRange?.start, end: timeRange?.end }, + eoTransformKeys: [ + 'enable_mcp', + 'subscriber_num', + 'api_num', + 'service_kind', + 'avaliable_monitor', + 'request_overview', + 'traffic_overview', + 'avg_request_per_subscriber_overview', + 'avg_response_time_overview', + 'avg_traffic_per_subscriber_overview', + 'request_total', + 'traffic_total', + 'avg_response_time', + 'avg_request_per_subscriber', + 'avg_traffic_per_subscriber' + ], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + enableMcp: true, + subscriberNum: 12, + apiNum: 3, + serviceKind: 'ai', + avaliableMonitor: false, + requestOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0, + fsdf: 4.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0, + fsdf: 5.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0, + fsdf: 6.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0, + fsdf: 7.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0, + fsdf: 8.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0, + fsdf: 9.0 + } + ], + trafficOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0 + } + ], + avgRequestPerSubscriberOverview: [1, 2, 3, 4, 5, 6], + avgResponseTimeOverview: [11, 231, 343, 1414, 25, 362], + avgTrafficPerSubscriberOverview: [4, 5, 1, 11, 4, 9], + requestTotal: '12 GB', + trafficTotal: '14 GB', + avgResponseTime: '1 k', + avgRequestPerSubscriber: '2 k', + avgTrafficPerSubscriber: '3 k', + date: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + } + // 存储 REST 服务数据 + setRestServiceOverview(serviceOverview) + // 设置 REST 报表数据 + setRestChartInfoData(serviceOverview) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + setDashboardLoading(false) + }) + } + + /** 获取排名列表 */ + const getTopRankingList = () => { + fetchData>('service/monitor/top10', { + method: 'GET', + eoParams: { service: serviceId, team: teamId, start: timeRange?.start, end: timeRange?.end }, + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + apis: [ + { + id: '123', + name: 'Model 1', + request: 100, + traffic: 100, + token: 100 + }, + { + id: '456', + name: 'Model 2', + request: 200, + traffic: 300, + token: 400 + } + ], + consumers: [ + { + id: '6666', + name: 'Customer 1', + request: 100, + traffic: 100, + token: 100 + } + ] + } + // 设置排名表格数据 + setTopRankingList({ + 'TOP API': serviceOverview.apis, + 'TOP Consumer': serviceOverview.consumers + }) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + setDashboardLoading(false) + }) + } + + useEffect(() => { + const { startTime, endTime } = getTime(defaultTime, []) + setTimeRange({ + start: startTime, + end: endTime + }) + }, []) + + useEffect(() => { + if (timeRange) { + serviceType === 'aiService' ? getAIServiceOverview() : getRestServiceOverview() + getTopRankingList() + } + }, [timeRange]) + + useEffect(() => { + if (serviceType === 'aiService') { + aiServiceOverview && setAiChartInfoData(aiServiceOverview) + } else { + restServiceOverview && setRestChartInfoData(restServiceOverview) + } + }, [state.language]) + + return ( + +
+ +
+
+ } + spinning={dashboardLoading} + > +
+ +
+ +
+
+ {barChartInfo?.map((item: BarChartInfo, index: number) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px]' + }} + > + + + ))} +
+
+ {perBarChartInfo?.map((item: any, index: number) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px]' + }} + > + {item.type === 'area' ? ( + <> + + + ) : ( + + )} + + ))} +
+ +
+ + ) +} + +export default ServiceOverview diff --git a/frontend/packages/core/src/pages/serviceOverview/utils.ts b/frontend/packages/core/src/pages/serviceOverview/utils.ts new file mode 100644 index 00000000..3581b38b --- /dev/null +++ b/frontend/packages/core/src/pages/serviceOverview/utils.ts @@ -0,0 +1,58 @@ +export type BarData = { + title: string + value: string + date: string[] + data: any[] +} +export const setBarChartInfoData = ({ title, value, data, date }: BarData) => { + // 首先获取所有的键名(假设所有对象的键名都一样) + if (data.length === 0) { + return { + title, + value, + date, + data: [] + } + } + if (typeof data[0] !== 'object') { + return { + title, + value, + date, + data + } + } + // 从第一个对象中获取所有键名 + const keys = Object.keys(data[0]) + // 定义颜色映射 + const colorMap: Record = { + '2xx': '#7EC26A', + '4xx': '#F2CF59', + '5xx': '#F17975', + '200': '#7EC26A', + '400': '#F2CF59', + '500': '#F17975' + } + + // 为每个键创建一个数据集 + const transformedData = keys.map((key, index) => { + // 为没有映射颜色的键生成随机颜色 + const color = + colorMap[key] || + `#${Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, '0')}` + + return { + name: key, + color: color, + value: data.map((item) => item[key]) + } + }) + return { + title, + value, + date, + data: transformedData + } +} diff --git a/frontend/packages/core/src/pages/system/SystemInsidePage.tsx b/frontend/packages/core/src/pages/system/SystemInsidePage.tsx index f7d53bca..041819b9 100644 --- a/frontend/packages/core/src/pages/system/SystemInsidePage.tsx +++ b/frontend/packages/core/src/pages/system/SystemInsidePage.tsx @@ -14,6 +14,7 @@ import { FC, useEffect, useMemo, useState } from 'react' import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom' import { SystemConfigFieldType } from '../../const/system/type.ts' import { useSystemContext } from '../../contexts/SystemContext.tsx' +import ServiceInfoCard from '@common/components/aoplatform/serviceInfoCard.tsx' const SystemInsidePage: FC = () => { const { message } = App.useApp() @@ -31,7 +32,7 @@ const SystemInsidePage: FC = () => { fetchData>('service/info', { method: 'GET', eoParams: { team: teamId, service: serviceId } - }).then(response => { + }).then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { setSystemInfo(data.service) @@ -47,7 +48,7 @@ const SystemInsidePage: FC = () => { fetchData>('service/router/define', { method: 'GET', eoParams: { service: serviceId, team: teamId } - }).then(response => { + }).then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { setApiPrefix(data.prefix) @@ -65,6 +66,7 @@ const SystemInsidePage: FC = () => { 'assets', null, [ + getItem({$t('总览')}, 'overview', undefined, undefined, undefined, ''), getItem( {$t('API 路由')}, 'route', @@ -146,9 +148,10 @@ const SystemInsidePage: FC = () => { null, [ // APP_MODE === 'pro' ? getItem({$t('调用拓扑图')}, 'topology',undefined,undefined,undefined,'project.mySystem.topology.view'):null, + getItem({$t('设置')}, 'setting', undefined, undefined, undefined, ''), getItem( - {$t('设置')}, - 'setting', + {$t('日志')}, + 'logs', undefined, undefined, undefined, @@ -166,12 +169,11 @@ const SystemInsidePage: FC = () => { const newMenu = cloneDeep(menu) return newMenu!.filter((m: MenuItemGroupType) => { if (m && m.children && m.children.length > 0) { - m.children = m.children.filter(c => { + m.children = m.children.filter((c) => { if (!c) return false return (c as MenuItemType & { access: string }).access ? checkPermission( - (c as MenuItemType & { access: string }) - .access as keyof (typeof PERMISSION_DEFINITION)[0] + (c as MenuItemType & { access: string }).access as keyof (typeof PERMISSION_DEFINITION)[0] ) : true }) @@ -180,12 +182,8 @@ const SystemInsidePage: FC = () => { }) } const filteredMenu = filterMenu(SYSTEM_PAGE_MENU_ITEMS as MenuItemGroupType[]) - const menu = - (activeMenu ?? filteredMenu[0]?.children) - ? filteredMenu[0]?.children?.[0]?.key - : filteredMenu[0]?.key - if (menu && currentUrl.split('/')[-1] !== menu) - navigateTo(`/service/${teamId}/inside/${serviceId}/${menu}`) + const menu = (activeMenu ?? filteredMenu[0]?.children) ? filteredMenu[0]?.children?.[0]?.key : filteredMenu[0]?.key + if (menu && currentUrl.split('/')[-1] !== menu) navigateTo(`/service/${teamId}/inside/${serviceId}/${menu}`) return filteredMenu || [] }, [accessData, accessInit, SYSTEM_PAGE_MENU_ITEMS]) @@ -208,7 +206,7 @@ const SystemInsidePage: FC = () => { } else if (serviceId !== currentUrl.split('/')[currentUrl.split('/').length - 1]) { setActiveMenu(currentUrl.split('/')[currentUrl.split('/').length - 1]) } else { - setActiveMenu('route') + setActiveMenu('overview') } }, [currentUrl]) @@ -233,16 +231,7 @@ const SystemInsidePage: FC = () => { {showMenu ? ( - {$t('服务 ID')}:{serviceId || '-'} - - ) - } - ]} + customBanner={} backUrl="/service/list" >
diff --git a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx index 7c322233..2ee65308 100644 --- a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx +++ b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx @@ -1,89 +1,578 @@ import { useNavigate } from 'react-router-dom' -import MonitorTotalPage from '@dashboard/component/MonitorTotalPage' -import { BasicResponse } from '@common/const/const' -import { - InvokeData, - MessageData, - MonitorApiData, - MonitorSubscriberData, - PieData, - SearchBody -} from '@dashboard/const/type' import { useFetch } from '@common/hooks/http' -import { objectToSearchParameters } from '@common/utils/router' +import ScrollableSection from '@common/components/aoplatform/ScrollableSection' +import { TimeRange } from '@common/components/aoplatform/TimeRangeSelector' +import { useEffect, useState } from 'react' +import DateSelectFilter, { TimeOption } from '@core/pages/serviceOverview/filter/DateSelectFilter' +import { getTime } from '@dashboard/utils/dashboard' +import { $t } from '@common/locales/index.ts' +import { LoadingOutlined } from '@ant-design/icons' +import { Card, Spin } from 'antd' +import ServiceBarChar, { BarChartInfo } from '@core/pages/serviceOverview/charts/ServiceBarChar' +import ServiceAreaChart from '@core/pages/serviceOverview/charts/ServiceAreaChart' +import RankingList from '@core/pages/serviceOverview/rankingList/RankingList' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { setBarChartInfoData } from '@core/pages/serviceOverview/utils' +import { App } from 'antd' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' + export default function DashboardTotal() { + /** 获取数据 */ const { fetchData } = useFetch() - const navigateTo = useNavigate() - const fetchPieData: (body: SearchBody) => Promise> = (body: SearchBody) => - fetchData>('monitor/overview/summary', { - method: 'POST', - eoBody: body, - eoTransformKeys: ['request_summary', 'proxy_summary'] - }) - - const fetchInvokeData: (body: SearchBody) => Promise> = (body: SearchBody) => - fetchData>('monitor/overview/invoke', { - method: 'POST', - eoBody: body, - eoTransformKeys: ['request_total', 'request_rate', 'proxy_total', 'proxy_rate', 'time_interval'] - }) - - const fetchMessageData: (body: SearchBody) => Promise> = (body: SearchBody) => - fetchData>('monitor/overview/message', { - method: 'POST', - eoBody: body, - eoTransformKeys: ['time_interval', 'request_message', 'response_message'] - }) - - const fetchTableData: ( - body: SearchBody, - type: 'api' | 'subscribers' | 'providers' - ) => Promise> = ( - body: SearchBody, - type: 'api' | 'subscribers' | 'providers' - ) => - fetchData>('monitor/overview/top10', { - method: 'POST', - eoBody: { ...body, dataType: type }, - eoTransformKeys: [ - 'dataType', - 'request_total', - 'request_success', - 'request_rate', - 'proxy_total', - 'proxy_success', - 'proxy_rate', - 'status_fail', - 'avg_resp', - 'max_resp', - 'min_resp', - 'avg_traffic', - 'max_traffic', - 'min_traffic', - 'min_traffic', - 'is_red' - ] - }) - - const goToDetail: (body: SearchBody, val: MonitorApiData | MonitorSubscriberData, type: string) => void = ( - body: SearchBody, - val: MonitorApiData | MonitorSubscriberData, - type: string - ) => { - // ...跳转到详情页... - const { start: startTime, end: endTime, clusters } = body - navigateTo( - `/analytics/${type}/list?${objectToSearchParameters({ id: val.id, clusters: clusters || undefined, start: startTime?.toString(), end: endTime?.toString(), name: val.name }).toString()}` - ) + /** 默认时间 */ + const [defaultTime] = useState('sevenDays') + /** 当前选中的时间范围 */ + const [timeRange, setTimeRange] = useState() + /** 当前激活的标签 */ + const [activeTab, setActiveTab] = useState('REST') + /** 面板 loading */ + const [dashboardLoading, setDashboardLoading] = useState(false) + /** 总数数据 */ + const [barChartInfo, setBarChartInfo] = useState() + /** 平均值数据 */ + const [perBarChartInfo, setPerBarChartInfo] = useState() + /** 排名表格数据 */ + const [topRankingList, setTopRankingList] = useState([]) + /** 弹窗组件 */ + const { message } = App.useApp() + /** 全局状态 */ + const { state } = useGlobalContext() + /** AI 服务数据 */ + const [aiServiceOverview, setAiServiceOverview] = useState() + /** REST 服务数据 */ + const [restServiceOverview, setRestServiceOverview] = useState() + /** 时间选择回调 */ + const selectCallback = (date: TimeRange) => { + setTimeRange(date) } + /** 获取 AI 服务信息 */ + const getAIServiceOverview = () => { + return fetchData>('monitor/overview/chart/ai', { + method: 'GET', + eoParams: { start: timeRange?.start, end: timeRange?.end }, + eoTransformKeys: [ + 'request_overview', + 'token_overview', + 'avg_token_overview', + 'avg_request_per_subscriber_overview', + 'avg_token_per_subscriber_overview', + 'request_total', + 'token_total', + 'avg_token', + 'avg_request_per_subscriber', + 'avg_token_per_subscriber' + ], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + requestOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0, + fsdf: 4.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0, + fsdf: 5.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0, + fsdf: 6.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0, + fsdf: 7.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0, + fsdf: 8.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0, + fsdf: 9.0 + } + ], + tokenOverview: [ + { + '2xx': 1.0, + '4xx': 2.0, + '5xx': 3.0 + }, + { + '2xx': 2.0, + '4xx': 3.0, + '5xx': 4.0 + }, + { + '2xx': 3.0, + '4xx': 4.0, + '5xx': 5.0 + }, + { + '2xx': 4.0, + '4xx': 5.0, + '5xx': 6.0 + }, + { + '2xx': 5.0, + '4xx': 6.0, + '5xx': 7.0 + }, + { + '2xx': 6.0, + '4xx': 7.0, + '5xx': 8.0 + } + ], + avgTokenOverview: [11, 231, 343, 1414, 25, 362], + avgRequestPerSubscriberOverview: [1, 2, 3, 4, 5, 6], + avgTokenPerSubscriberOverview: [4, 5, 1, 11, 4, 9], + requestTotal: '12 GB', + tokenTotal: '14 GB', + avgToken: '1 k', + avgRequestPerSubscriber: '2 k', + avgTokenPerSubscriber: '3 k', + date: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + } + // 存储 AI 服务数据 + setAiServiceOverview(serviceOverview) + // 设置 AI 报表数据 + setAiChartInfoData(serviceOverview) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } + + /** 获取 REST 服务信息 */ + const getRestServiceOverview = () => { + return fetchData>('monitor/overview/chart/rest', { + method: 'GET', + eoParams: { start: timeRange?.start, end: timeRange?.end }, + eoTransformKeys: [ + 'request_overview', + 'traffic_overview', + 'avg_request_per_subscriber_overview', + 'avg_response_time_overview', + 'avg_traffic_per_subscriber_overview', + 'request_total', + 'traffic_total', + 'avg_response_time', + 'avg_request_per_subscriber', + 'avg_traffic_per_subscriber' + ], + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const serviceOverview = { + requestOverview: [ + { + '2xx': 33.0, + '4xx': 44.0, + '5xx': 5.0, + fsdf: 6.0 + }, + { + '2xx': 123.0, + '4xx': 324.0, + '5xx': 112.0, + fsdf: 44.0 + }, + { + '2xx': 234.0, + '4xx': 436.0, + '5xx': 123.0, + fsdf: 4.0 + }, + { + '2xx': 4.0, + '4xx': 234.0, + '5xx': 1233.0, + fsdf: 7.0 + }, + { + '2xx': 5.0, + '4xx': 233.0, + '5xx': 7123.0, + fsdf: 8.0 + }, + { + '2xx': 444.0, + '4xx': 7.0, + '5xx': 8.0, + fsdf: 9.0 + } + ], + trafficOverview: [ + { + '2xx': 1123.0, + '4xx': 23.0, + '5xx': 3.0 + }, + { + '2xx': 112.0, + '4xx': 233.0, + '5xx': 44.0 + }, + { + '2xx': 3.0, + '4xx': 1234.0, + '5xx': 445.0 + }, + { + '2xx': 14.0, + '4xx': 2345.0, + '5xx': 6.0 + }, + { + '2xx': 132.0, + '4xx': 346.0, + '5xx': 37.0 + }, + { + '2xx': 613.0, + '4xx': 47.0, + '5xx': 81.0 + } + ], + avgRequestPerSubscriberOverview: [345, 23, 12, 123, 43, 2], + avgResponseTimeOverview: [123, 232, 443, 54, 125, 61], + avgTrafficPerSubscriberOverview: [44, 235, 11, 114, 234, 239], + requestTotal: '11 GB', + trafficTotal: '22 GB', + avgResponseTime: '33 k', + avgRequestPerSubscriber: '44 k', + avgTrafficPerSubscriber: '55 k', + date: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + } + // 设置 REST 服务数据 + setRestServiceOverview(serviceOverview) + // 存储 REST 报表数据 + setRestChartInfoData(serviceOverview) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } + + /** + * 设置 REST 服务数据 + * */ + const setRestChartInfoData = (serviceOverview: any) => { + // 设置总数数据 + setBarChartInfo([ + // 服务请求次数 + setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + // 流量消耗总数 + setBarChartInfoData({ + title: $t('流量'), + data: serviceOverview.trafficOverview, + value: serviceOverview.trafficTotal, + date: serviceOverview.date + }) + ]) + // 设置平均值数据 + setPerBarChartInfo([ + // 各个模型使用量 + { + title: $t('平均响应时间'), + data: serviceOverview.avgResponseTimeOverview, + value: serviceOverview.avgResponseTime, + date: serviceOverview.date, + type: 'area' + }, + // 平均请求 + setBarChartInfoData({ + title: $t('平均请求数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + value: serviceOverview.avgRequestPerSubscriber, + date: serviceOverview.date + }), + // 平均流量消耗 + setBarChartInfoData({ + title: $t('平均流量'), + data: serviceOverview.avgTrafficPerSubscriberOverview, + value: serviceOverview.avgTrafficPerSubscriber, + date: serviceOverview.date + }) + ]) + } + + /** + * 设置 AI 服务数据 + * */ + const setAiChartInfoData = (serviceOverview: any) => { + // 设置总数数据 + setBarChartInfo([ + // 服务请求次数 + setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + // token 消耗总数 + setBarChartInfoData({ + title: $t('Token'), + data: serviceOverview.tokenOverview, + value: serviceOverview.tokenTotal, + date: serviceOverview.date + }) + ]) + // 设置平均值数据 + setPerBarChartInfo([ + // 平均 token 消耗 + { + title: $t('平均 Token/s 统计'), + data: serviceOverview.avgTokenOverview, + value: serviceOverview.avgToken, + date: serviceOverview.date, + type: 'area' + }, + // 平均请求 + setBarChartInfoData({ + title: $t('平均请求数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + value: serviceOverview.avgRequestPerSubscriber, + date: serviceOverview.date + }), + // 平均 token 消耗 + setBarChartInfoData({ + title: $t('平均 Token/订阅者统计'), + data: serviceOverview.avgTokenPerSubscriberOverview, + value: serviceOverview.avgTokenPerSubscriber, + date: serviceOverview.date + }) + ]) + } + + /** 获取排名列表 */ + const getTopRankingList = () => { + return fetchData>( + `monitor/overview/top10/${activeTab === 'AI' ? 'ai' : 'rest'}`, + { + method: 'GET', + eoParams: { start: timeRange?.start, end: timeRange?.end }, + eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + } + ).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + const aiServiceOverview = { + apis: [ + { + id: '123', + name: 'Model 21', + request: 100, + token: 100 + }, + { + id: '456', + name: 'Model 22', + request: 200, + token: 400 + }, + { + id: '45611', + name: 'Model 3', + request: 3200, + token: 4400 + }, + { + id: '4536', + name: 'Model 4', + request: 1200, + token: 4200 + } + ], + consumers: [ + { + id: '6666', + name: 'Customer 1', + request: 100, + token: 100 + } + ] + } + const restServiceOverview = { + apis: [ + { + id: '123', + name: 'Model 1', + request: 100, + traffic: 100 + }, + { + id: '456', + name: 'Model 2', + request: 200, + traffic: 300 + }, + { + id: '12333', + name: 'Model 123', + request: 200, + traffic: 300 + } + ], + consumers: [ + { + id: '6666', + name: 'Customer 1', + request: 100, + traffic: 100 + } + ] + } + // 设置排名表格数据 + setTopRankingList({ + 'TOP API': activeTab === 'AI' ? aiServiceOverview.apis : restServiceOverview.apis, + 'TOP Consumer': activeTab === 'AI' ? aiServiceOverview.consumers : restServiceOverview.consumers + }) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } + + useEffect(() => { + const { startTime, endTime } = getTime(defaultTime, []) + setTimeRange({ + start: startTime, + end: endTime + }) + }, []) + useEffect(() => { + const fetchData = async () => { + setDashboardLoading(true) + try { + const requests = [] + // 根据activeTab添加相应的请求 + if (activeTab === 'AI') { + requests.push(getAIServiceOverview()) + } else { + requests.push(getRestServiceOverview()) + } + + // 添加排名列表请求 + requests.push(getTopRankingList()) + + // 等待所有请求完成 + await Promise.all(requests) + } catch (error) { + console.error('加载数据出错:', error) + message.error($t('加载数据失败,请重试')) + } finally { + // 无论成功失败,最后都设置loading为false + setDashboardLoading(false) + } + } + + if (timeRange) { + fetchData() + } + }, [timeRange, activeTab]) + useEffect(() => { + if (activeTab === 'AI') { + aiServiceOverview && setAiChartInfoData(aiServiceOverview) + } else { + restServiceOverview && setRestChartInfoData(restServiceOverview) + } + }, [state.language]) + return ( - +
+ +
+
{$t('服务')}:
+
+
setActiveTab('REST')} + > + REST +
+
setActiveTab('AI')} + > + AI +
+
+ +
+ +
+ +
+
+ } + spinning={dashboardLoading} + > +
+ {barChartInfo?.map((item: BarChartInfo, index: number) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px]' + }} + > + + + ))} +
+
+ {perBarChartInfo?.map((item: any, index: number) => ( + 0 ? 'ml-[10px]' : ''}`} + classNames={{ + body: 'p-[15px]' + }} + > + {item.type === 'area' ? ( + <> + + + ) : ( + + )} + + ))} +
+ + + +
) } diff --git a/frontend/packages/market/src/pages/serviceHub/ServiceHubDetail.tsx b/frontend/packages/market/src/pages/serviceHub/ServiceHubDetail.tsx index 82e41dd5..8248dcaf 100644 --- a/frontend/packages/market/src/pages/serviceHub/ServiceHubDetail.tsx +++ b/frontend/packages/market/src/pages/serviceHub/ServiceHubDetail.tsx @@ -20,6 +20,7 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js' import McpToolsContainer from '@core/pages/mcpService/McpToolsContainer.tsx' import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx' import TopBreadcrumb from '@common/components/aoplatform/Breadcrumb.tsx' +import ServiceInfoCard from '@common/components/aoplatform/serviceInfoCard.tsx' type TabItemType = { key: string @@ -32,18 +33,12 @@ const ServiceHubDetail = () => { const { serviceId } = useParams() const { setBreadcrumb } = useBreadcrumb() const [serviceBasicInfo, setServiceBasicInfo] = useState() - const [serviceName, setServiceName] = useState() - const [serviceDesc, setServiceDesc] = useState() const [serviceDoc, setServiceDoc] = useState() const { fetchData } = useFetch() const applyRef = useRef(null) const { modal, message } = App.useApp() const [mySystemOptionList, setMySystemOptionList] = useState() const [service, setService] = useState() - const [serviceMetrics, setServiceMetrics] = useState<{ title: string; icon: React.ReactNode; value: string }[]>([]) - const [serviceTags, setServiceTags] = useState< - { color: string; textColor: string; title: string; content: React.ReactNode }[] - >([]) const [tools, setTools] = useState([]) const [tabItem, setTabItem] = useState([]) const [currentTab, setCurrentTab] = useState('') @@ -149,10 +144,7 @@ servers: apiDoc: modifyApiDoc(data.service.apiDoc, data.service.basic?.invokeAddress) }) setServiceBasicInfo(data.service.basic) - setServiceName(data.service.name) - setServiceDesc(data.service.description) setServiceDoc(DOMPurify.sanitize(data.service.document)) - setServiceMetricsList(data.service.basic) setTabItemList(data.service.basic) } else { message.error(msg || $t(RESPONSE_TIPS.error)) @@ -164,54 +156,6 @@ servers: setCurrentTab(value) } - const setServiceMetricsList = (serviceBasicInfo: ServiceBasicInfoType) => { - // 设置服务指标数据 - setServiceMetrics([ - { - title: 'API 数量', - icon: , - value: serviceBasicInfo.apiNum.toString() - }, - { - title: '接入消费者数量', - icon: , - value: serviceBasicInfo.appNum.toString() - }, - { - title: '30天内调用次数', - icon: , - value: formatInvokeCount(serviceBasicInfo.invokeCount ?? 0) - } - ]) - // 设置服务标签数据 - const tags = [ - { - color: '#7371fc1b', - textColor: 'text-theme', - title: serviceBasicInfo?.catalogue?.name || '-', - content: serviceBasicInfo?.catalogue?.name || '-' - }, - { - color: `#${serviceBasicInfo?.serviceKind === 'ai' ? 'EADEFF' : 'DEFFE7'}`, - textColor: 'text-[#000]', - title: serviceBasicInfo?.serviceKind || '-', - content: SERVICE_KIND_OPTIONS.find((x) => x.value === serviceBasicInfo?.serviceKind)?.label || '-' - } - ] - - // 如果启用了MCP,添加MCP标签 - if (serviceBasicInfo?.enableMcp) { - tags.push({ - color: '#FFF0C1', - textColor: 'text-[#000]', - title: 'MCP', - content: 'MCP' - }) - } - - setServiceTags(tags) - } - useEffect(() => { if (!serviceId) { console.warn('缺少serviceId') @@ -270,7 +214,7 @@ servers: content: ( ), @@ -293,19 +237,6 @@ servers: const handleToolsChange = (value: Tool[]) => { setTools(value) } - // 格式化调用次数,添加K和M单位 - const formatInvokeCount = (count: number | null | undefined): string => { - if (count === null || count === undefined) return '-' - if (count >= 1000000) { - const value = Math.floor(count / 100000) / 10 - return `${value}M` - } - if (count >= 1000) { - const value = Math.floor(count / 100) / 10 - return `${value}K` - } - return count.toString() - } /** * 定义一个更新标签项的函数,在serviceBasicInfo或tools变化时调用 @@ -431,74 +362,21 @@ servers:
navigate(`/serviceHub/list`)} />
- -
-
-
- - ) : undefined - } - icon={serviceBasicInfo?.logo ? '' : } - > - {' '} - -
-
-

- {serviceName} -

-
- {serviceTags.map((tag, index) => ( - - {tag.content} - - ))} - {serviceMetrics.map((item, index) => ( - - - {item.icon} - {item.value} - - - ))} -
-
-
- - {serviceDesc || $t('暂无服务描述')} - -
-
- -
-
+ customClassName="mt-[20px]" + actionSlot={ + <> + + + } + />
Date: Sun, 27 Apr 2025 10:43:06 +0800 Subject: [PATCH 02/29] feature/1.8-Improve system observability --- .../common/src/components/aoplatform/PageList.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/PageList.tsx b/frontend/packages/common/src/components/aoplatform/PageList.tsx index dab31d82..d81ce14e 100644 --- a/frontend/packages/common/src/components/aoplatform/PageList.tsx +++ b/frontend/packages/common/src/components/aoplatform/PageList.tsx @@ -58,7 +58,7 @@ interface PageListProps extends ProTableProps, RefAttributes void, + manualReloadTable?: () => void customEmptyRender?: () => React.ReactNode } @@ -109,6 +109,7 @@ const PageList = >( const [allowTableClick, setAllowTableClick] = useState(false) const { accessData, checkPermission, accessInit, state } = useGlobalContext() const [minTableWidth, setMinTableWidth] = useState(0) + const [enableVirtual, setEnableVirtual] = useState(false) useImperativeHandle(ref, () => actionRef.current!) @@ -301,7 +302,7 @@ const PageList = >( actionRef={actionRef} columns={newColumns} - virtual + virtual={enableVirtual} scroll={noScroll ? undefined : { x: tableWidth, y: tableHeight }} size="middle" rowSelection={rowSelection} @@ -328,6 +329,10 @@ const PageList = >( } : false }} + postData={(data: any) => { + setEnableVirtual(!!data?.length) + return data + }} showSorterTooltip={false} columnsState={{ persistenceType: 'localStorage', persistenceKey: id }} pagination={ From 2e18e1f8a4bedb99ee48d078432c5ab7d863590f Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Sun, 27 Apr 2025 10:53:27 +0800 Subject: [PATCH 03/29] feature/1.8-Improve system observability --- frontend/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 0f6fe4b5..24db0635 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -50,7 +50,8 @@ "vite-tsconfig-paths": "^4.3.2", "react-json-view": "^1.21.3", "zod": "^3.23.8", - "@modelcontextprotocol/sdk": "^1.9.0" + "@modelcontextprotocol/sdk": "^1.9.0", + "echarts-for-react": "^3.0.2" }, "devDependencies": { "@ant-design/cssinjs": "^1.18.2", From 85130ad8822222c1beaa025cce3f45613af3adcb Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Sun, 27 Apr 2025 14:14:52 +0800 Subject: [PATCH 04/29] feature/1.8-Improve system observability --- .../common/src/contexts/BreadcrumbContext.tsx | 6 +++-- .../common/src/locales/keyHashMap.json | 22 +++++++--------- .../common/src/locales/scan/en-US.json | 7 ++--- .../common/src/locales/scan/ja-JP.json | 7 ++--- .../common/src/locales/scan/zh-CN.json | 7 ++--- .../common/src/locales/scan/zh-TW.json | 7 ++--- .../core/src/const/ai-service/const.tsx | 11 ++++---- .../packages/core/src/const/system/const.tsx | 6 +++++ .../packages/core/src/const/system/type.ts | 1 + .../aiService/AiServiceInsideDocument.tsx | 14 +--------- .../pages/aiService/AiServiceInsidePage.tsx | 15 +++++++++-- .../aiService/AiServiceInsideSubscriber.tsx | 14 +--------- .../api/AiServiceInsideApiDocument.tsx | 14 +--------- .../api/AiServiceInsideRouterCreate.tsx | 13 ---------- .../api/AiServiceInsideRouterList.tsx | 13 ---------- .../approval/AiServiceInsideApprovalList.tsx | 12 --------- .../publish/AiServiceInsidePublish.tsx | 14 ---------- .../publish/AiServiceInsidePublishList.tsx | 12 --------- .../src/pages/policy/ServicePolicyLayout.tsx | 12 --------- .../policy/dataMasking/DataMaskingConfig.tsx | 13 ---------- .../core/src/pages/system/SystemConfig.tsx | 11 -------- .../src/pages/system/SystemInsideDocument.tsx | 14 +--------- .../src/pages/system/SystemInsidePage.tsx | 15 +++++++++-- .../pages/system/SystemInsideSubscriber.tsx | 14 +--------- .../core/src/pages/system/SystemTopology.tsx | 14 +--------- .../system/api/SystemInsideApiDocument.tsx | 14 +--------- .../system/api/SystemInsideRouterCreate.tsx | 26 +++++++------------ .../system/api/SystemInsideRouterList.tsx | 15 +---------- .../approval/SystemInsideApprovalList.tsx | 14 +--------- .../system/publish/SystemInsidePublish.tsx | 16 +----------- .../publish/SystemInsidePublishList.tsx | 14 +--------- .../upstream/SystemInsideUpstreamContent.tsx | 16 ++---------- 32 files changed, 90 insertions(+), 313 deletions(-) diff --git a/frontend/packages/common/src/contexts/BreadcrumbContext.tsx b/frontend/packages/common/src/contexts/BreadcrumbContext.tsx index 2075b855..e88c2eb6 100644 --- a/frontend/packages/common/src/contexts/BreadcrumbContext.tsx +++ b/frontend/packages/common/src/contexts/BreadcrumbContext.tsx @@ -22,8 +22,10 @@ export const BreadcrumbProvider = ({ children }: unknown) => { { - newItems.slice(0, newItems.length - 1).forEach((item) => { - item.title = {item.title} + newItems.forEach((item) => { + item.title = ( + {item.title} + ) }) setBreadcrumb(newItems) }, diff --git a/frontend/packages/common/src/locales/keyHashMap.json b/frontend/packages/common/src/locales/keyHashMap.json index 1d283fed..f796c540 100644 --- a/frontend/packages/common/src/locales/keyHashMap.json +++ b/frontend/packages/common/src/locales/keyHashMap.json @@ -350,13 +350,13 @@ "重置": "K50d471b2", "查询": "Kee8ae330", "请输入 APIURL 搜索": "Kf8187c33", - "服务": "Kb58e0c3f", - "使用说明": "Kdefa9caa", "最近一次更新者": "K617f34f1", "最近一次更新时间": "K6ebca204", "保存": "Kabfe9512", + "服务": "Kb58e0c3f", "API 路由": "K51d1eb5d", - "API 文档": "Ka2b6d281", + "API 路由文档": "K1639a17a", + "使用说明": "Kdefa9caa", "服务策略": "K52f72551", "发布": "K36856e71", "订阅管理": "K6382bbfd", @@ -369,9 +369,6 @@ "手动添加": "K18307d56", "订阅申请": "K705fe9f5", "订阅方": "K3a67ea90", - "API": "K3ba29a85", - "编辑 API": "Ke93388fd", - "添加 API": "K84aabfd4", "AI 路由设置": "Kefa2a4cf", "路由名称": "K66060758", "请求路径": "K5582ac8", @@ -382,7 +379,6 @@ "拦截接口": "Kee4139c2", "开启拦截后,网关会拦截所有该路径的请求。": "K3e38ea", "模型配置": "K8a35059b", - "路由": "Kf9dcef3a", "添加路由": "K6134bbe8", "输入 URL 查找路由": "Kf85b83a0", "线上模型": "K84b2cf2d", @@ -633,8 +629,6 @@ "处理日志": "Ke429194e", "脱敏前": "K8c34c02f", "脱敏后": "K8e3d388d", - "编辑服务策略": "Kf06f6737", - "添加服务策略": "K205971e1", "编辑策略": "Kc82b8374", "策略类型": "K4b34a5e5", "匹配条件": "K57f0fee8", @@ -678,13 +672,13 @@ "API 使用排名": "Kbee2340", "消费者使用排名": "Kf6af1f40", "请求数": "K318a7519", - "Token": "K9ef68e3f", - "平均 Token/s 统计": "K6c016898", - "平均请求数": "K652843b0", - "平均 Token/订阅者统计": "Kf5eeb9c5", "流量": "K53eb7414", "平均响应时间": "K7c8d5c23", + "平均请求数": "K652843b0", "平均流量": "K8158a6e4", + "Token": "K9ef68e3f", + "平均 Token/s 统计": "K6c016898", + "平均 Token/订阅者统计": "Kf5eeb9c5", "单位:ms,最小值:1": "K2a16c93b", "API 路由设置": "Ka945cfb1", "API 基础信息": "K2e050340", @@ -861,6 +855,7 @@ "请确认是否取消订阅申请?": "K1856c229", "搜索服务": "K66ea2f0", "审核中": "K8adf7f8b", + "API 文档": "Ka2b6d281", "添加消费者": "K84c4dc71", "暂无消费者描述": "Kc3b7bfa8", "创建并管理自己的消费者实体,每个消费者可以订阅多个API服务,确保在调用之前已获得相应权限。你可以为消费者生成 API 密钥等鉴权方式,用于安全地调用 API 服务": "K5c4e2865", @@ -873,6 +868,7 @@ "版本": "K81634069", "更新时间": "Keefda53d", "介绍": "K59cdbec3", + "API": "K3ba29a85", "无标签": "K96a2f1c8", "分类": "Kb32f0afe", "服务市场": "K370a3eb2", diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 936e9eb5..92be8d7f 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -186,7 +186,7 @@ "K617f34f1": "Updated By", "K6ebca204": "Update Time", "Kabfe9512": "Save", - "K51d1eb5d": "API", + "K51d1eb5d": "API Routes", "Ka2b6d281": "API Docs", "Kdefa9caa": "Usage Instructions", "K36856e71": "Publish", @@ -210,7 +210,7 @@ "K469e475a": "Max Retry Times", "K8a35059b": "Model Settings", "Kf9dcef3a": "API", - "K6134bbe8": "Add API", + "K6134bbe8": "Add API Route", "Kf85b83a0": "Enter URL to Search", "Kcf9f90b8": "Model Provider", "Kfede1c7c": "Model", @@ -956,5 +956,6 @@ "K8158a6e4": "Avg Traffic per Subscriber", "K6b882d4a": "Avg Token per Subscriber", "K6c2d93b6": "Failed to load data, please try again", - "Kf5eeb9c5": "Avg Token per Subscriber" + "Kf5eeb9c5": "Avg Token per Subscriber", + "K1639a17a": "API Route Docs" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index dff7d694..fff795c6 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -189,7 +189,7 @@ "K617f34f1": "更新者", "K6ebca204": "更新日時", "Kabfe9512": "保存", - "K51d1eb5d": "API", + "K51d1eb5d": "APIルート", "Ka2b6d281": "API ドキュメント", "Kdefa9caa": "説明ドキュメント", "K36856e71": "公開", @@ -213,7 +213,7 @@ "K469e475a": "リトライ回数", "K8a35059b": "モデル設定", "Kf9dcef3a": "API", - "K6134bbe8": "API を追加", + "K6134bbe8": "APIルートを追加する", "Kf85b83a0": "URL を入力して検索", "Kcf9f90b8": "モデルプロバイダー", "Kfede1c7c": "モデル", @@ -978,5 +978,6 @@ "K8158a6e4": "平均トラフィック", "K6b882d4a": "平均トークン/加入者", "K6c2d93b6": "データの読み込みに失敗しました。もう一度お試しください", - "Kf5eeb9c5": "平均トークン/加入者 統計" + "Kf5eeb9c5": "平均トークン/加入者 統計", + "K1639a17a": "APIルートのドキュメント" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index 99343db3..f86c8f44 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -189,7 +189,7 @@ "K617f34f1": "更新者", "K6ebca204": "更新时间", "Kabfe9512": "保存", - "K51d1eb5d": "API", + "K51d1eb5d": "API 路由", "Ka2b6d281": "API 文档", "Kdefa9caa": "说明文档", "K36856e71": "发布", @@ -213,7 +213,7 @@ "K469e475a": "最大重试次数", "K8a35059b": "模型设置", "Kf9dcef3a": "API", - "K6134bbe8": "添加 API", + "K6134bbe8": "添加 API 路由", "Kf85b83a0": "输入 URL 查找", "Kcf9f90b8": "模型供应商", "Kfede1c7c": "模型", @@ -907,5 +907,6 @@ "K652843b0": "平均请求数", "K8158a6e4": "平均流量", "K6c2d93b6": "加载数据失败,请重试", - "Kf5eeb9c5": "平均 Token/订阅者统计" + "Kf5eeb9c5": "平均 Token/订阅者统计", + "K1639a17a": "API 路由文档" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index 9afebe7d..9a30af38 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -189,7 +189,7 @@ "K617f34f1": "更新者", "K6ebca204": "更新時間", "Kabfe9512": "保存", - "K51d1eb5d": "API", + "K51d1eb5d": "API 路由", "Ka2b6d281": "API 文檔", "Kdefa9caa": "說明文檔", "K36856e71": "發布", @@ -213,7 +213,7 @@ "K469e475a": "最大重試次數", "K8a35059b": "模型設置", "Kf9dcef3a": "API", - "K6134bbe8": "添加 API", + "K6134bbe8": "添加 API 路由", "Kf85b83a0": "輸入 URL 查找", "Kcf9f90b8": "模型供應商", "Kfede1c7c": "模型", @@ -978,5 +978,6 @@ "K8158a6e4": "平均流量", "K6b882d4a": "每位訂閱者平均 Token", "K6c2d93b6": "載入資料失敗,請重試", - "Kf5eeb9c5": "每位訂閱者平均 Token 統計" + "Kf5eeb9c5": "每位訂閱者平均 Token 統計", + "K1639a17a": "API 路由文件" } diff --git a/frontend/packages/core/src/const/ai-service/const.tsx b/frontend/packages/core/src/const/ai-service/const.tsx index a058f636..fad67f2b 100644 --- a/frontend/packages/core/src/const/ai-service/const.tsx +++ b/frontend/packages/core/src/const/ai-service/const.tsx @@ -6,17 +6,18 @@ import { AiServiceRouterTableListItem, VariableItems } from './type' import { PageProColumns } from '@common/components/aoplatform/PageList' export const AI_SERVICE_ROUTER_TABLE_COLUMNS: PageProColumns[] = [ + { + title: '名称', + dataIndex: 'name', + width: 200, + ellipsis: true + }, { title: 'URL', dataIndex: 'requestPath', ellipsis: true, width: 200 }, - { - title: '名称', - dataIndex: 'name', - ellipsis: true - }, { title: '模型', dataIndex: ['model', 'name'], diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index 214a35b6..cabbf89a 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -242,6 +242,12 @@ export const MATCH_CONFIG: ConfigField[] = [ ] export const SYSTEM_API_TABLE_COLUMNS: PageProColumns[] = [ + { + title: '名称', + dataIndex: 'name', + width: 200, + ellipsis: true + }, { title: 'URL', dataIndex: 'requestPath', diff --git a/frontend/packages/core/src/const/system/type.ts b/frontend/packages/core/src/const/system/type.ts index 9d4a82fe..0f1d70a2 100644 --- a/frontend/packages/core/src/const/system/type.ts +++ b/frontend/packages/core/src/const/system/type.ts @@ -97,6 +97,7 @@ export type SystemApiProxyType = { export type SystemApiProxyFieldType = { protocols: string[]; id:string; + name:string description?:string; disable:boolean; path:string; diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsideDocument.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsideDocument.tsx index e9059968..b39f14ee 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsideDocument.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsideDocument.tsx @@ -8,9 +8,8 @@ import { App, Button } from 'antd' import { EntityItem } from '@common/const/type.ts' import WithPermission from '@common/components/aoplatform/WithPermission.tsx' import { RouterParams } from '@core/components/aoplatform/RenderRoutes' -import { useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { $t } from '@common/locales' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' const ServiceInsideDocument = () => { const { message } = App.useApp() const [updater, setUpdater] = useState() @@ -19,8 +18,6 @@ const ServiceInsideDocument = () => { const [doc, setDoc] = useState() const { fetchData } = useFetch() const { serviceId, teamId } = useParams() - const { setBreadcrumb } = useBreadcrumb() - const navigator = useNavigate() const save = () => { fetchData< @@ -80,15 +77,6 @@ const ServiceInsideDocument = () => { } useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('使用说明') - } - ]) getServiceDoc() }, []) diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx index 0dc81acd..f6ea1d36 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx @@ -14,6 +14,7 @@ import { cloneDeep } from 'lodash-es' import { FC, useEffect, useMemo, useState } from 'react' import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom' import { useAiServiceContext } from '../../contexts/AiServiceContext.tsx' +import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' const APP_MODE = import.meta.env.VITE_APP_MODE const AiServiceInsidePage: FC = () => { @@ -27,6 +28,7 @@ const AiServiceInsidePage: FC = () => { const [activeMenu, setActiveMenu] = useState() const navigateTo = useNavigate() const [showMenu, setShowMenu] = useState(false) + const { setBreadcrumb } = useBreadcrumb() const getAiServiceInfo = () => { fetchData>('service/info', { @@ -77,7 +79,7 @@ const AiServiceInsidePage: FC = () => { 'team.service.router.view' ), getItem( - {$t('API 文档')}, + {$t('API 路由文档')}, 'api', undefined, undefined, @@ -215,10 +217,19 @@ const AiServiceInsidePage: FC = () => { }, [accessData]) useEffect(() => { + setBreadcrumb([ + { + title: $t('服务'), + onClick: () => navigateTo('/service/list') + }, + { + title: aiServiceInfo?.name || '' + } + ]) if (activeMenu && serviceId === currentUrl.split('/')[currentUrl.split('/').length - 1]) { navigateTo(`/service/${teamId}/aiInside/${serviceId}/${activeMenu}`) } - }, [activeMenu]) + }, [activeMenu, state.language, aiServiceInfo]) useEffect(() => { serviceId && getAiServiceInfo() diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsideSubscriber.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsideSubscriber.tsx index bb53dcae..a1789f41 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsideSubscriber.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsideSubscriber.tsx @@ -1,8 +1,7 @@ import {ActionType} from "@ant-design/pro-components"; import {FC, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from "react"; -import {Link, useNavigate, useParams} from "react-router-dom"; +import {useParams} from "react-router-dom"; import {App, Form,TreeSelect} from "antd"; -import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx"; import {useFetch} from "@common/hooks/http.ts"; import { RouterParams } from "@core/components/aoplatform/RenderRoutes.tsx"; import {BasicResponse, COLUMNS_TITLE, DELETE_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE} from "@common/const/const.tsx"; @@ -18,7 +17,6 @@ import { checkAccess } from "@common/utils/permission.ts"; import { $t } from "@common/locales/index.ts"; const AiServiceInsideSubscriber:FC = ()=>{ - const { setBreadcrumb } = useBreadcrumb() const { modal,message } = App.useApp() const {fetchData} = useFetch() const {serviceId, teamId} = useParams() @@ -26,7 +24,6 @@ const AiServiceInsideSubscriber:FC = ()=>{ const pageListRef = useRef(null); const [memberValueEnum, setMemberValueEnum] = useState([]) const {accessData,state} = useGlobalContext() - const navigator = useNavigate() const getAiServiceSubscriber = ()=>{ return fetchData>('service/subscribers',{method:'GET',eoParams:{service:serviceId,team:teamId},eoTransformKeys:['apply_time']}).then(response=>{ const {code,data,msg} = response @@ -120,15 +117,6 @@ const AiServiceInsideSubscriber:FC = ()=>{ ] useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title:$t('订阅方管理') - } - ]) getMemberList() manualReloadTable() }, [serviceId]); diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideApiDocument.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideApiDocument.tsx index 44a419b8..909696e2 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideApiDocument.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideApiDocument.tsx @@ -6,33 +6,21 @@ import { LoadingOutlined } from '@ant-design/icons' import EmptySVG from '@common/assets/empty.svg' import { $t } from '@common/locales/index.ts' import ApiDocument from '@common/components/aoplatform/ApiDocument.tsx' -import { useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx' import { AiServiceInsideApiDocumentHandle, AiServiceInsideApiDocumentProps, AiServiceApiDetail } from '@core/const/ai-service/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' const AiServiceInsideApiDocument = forwardRef(() => { const { serviceId, teamId } = useParams() const { fetchData } = useFetch() const [apiDetail, setApiDetail] = useState() const [loading, setLoading] = useState(false) - const { setBreadcrumb } = useBreadcrumb() - const navigator = useNavigate() useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('API 文档') - } - ]) getApiDetail() }, []) diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx index fa0ef96f..32bc732f 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx @@ -291,19 +291,6 @@ const AiServiceInsideRouterCreate = () => { } useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title:$t('API'), - onClick: () => navigator(backUrl) - }, - { - title: routeId ? $t('编辑 API') : $t('添加 API') - } - ]) !routeId && aiServiceInfo?.provider && getDefaultModelConfig() }, [aiServiceInfo]) diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterList.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterList.tsx index 55d2e3b0..7fe058df 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterList.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterList.tsx @@ -3,7 +3,6 @@ import PageList, { PageProColumns } from '@common/components/aoplatform/PageList import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission.tsx' import { BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const.tsx' import { SimpleMemberItem } from '@common/const/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx' import { useFetch } from '@common/hooks/http.ts' import { $t } from '@common/locales/index.ts' @@ -17,7 +16,6 @@ import { Link, useNavigate, useParams } from 'react-router-dom' const AiServiceInsideRouterList: FC = () => { const [searchWord, setSearchWord] = useState('') - const { setBreadcrumb } = useBreadcrumb() const { modal, message } = App.useApp() const [tableListDataSource, setTableListDataSource] = useState([]) const [tableHttpReload, setTableHttpReload] = useState(true) @@ -162,17 +160,6 @@ const AiServiceInsideRouterList: FC = () => { getMemberList() manualReloadTable() }, [serviceId]) - useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('路由') - } - ]) - }, [state.language]) const columns = useMemo(() => { return [...AI_SERVICE_ROUTER_TABLE_COLUMNS].map((x) => { diff --git a/frontend/packages/core/src/pages/aiService/approval/AiServiceInsideApprovalList.tsx b/frontend/packages/core/src/pages/aiService/approval/AiServiceInsideApprovalList.tsx index 6621ffec..13cc843a 100644 --- a/frontend/packages/core/src/pages/aiService/approval/AiServiceInsideApprovalList.tsx +++ b/frontend/packages/core/src/pages/aiService/approval/AiServiceInsideApprovalList.tsx @@ -3,7 +3,6 @@ import {ActionType} from "@ant-design/pro-components"; import {FC, useEffect, useMemo, useRef, useState} from "react"; import {Link, useLocation, useNavigate, useParams} from "react-router-dom"; import PageList, { PageProColumns } from "@common/components/aoplatform/PageList.tsx"; -import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx"; import {App, Button} from "antd"; import { SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN, @@ -26,7 +25,6 @@ import { SubscribeApprovalInfoType } from "@common/const/approval/type.tsx"; import { $t } from "@common/locales"; const AiServiceInsideApprovalList:FC = ()=>{ - const { setBreadcrumb } = useBreadcrumb() const { modal,message } = App.useApp() const {serviceId, teamId} = useParams(); const [init, setInit] = useState(true) @@ -40,7 +38,6 @@ const AiServiceInsideApprovalList:FC = ()=>{ const [approvalBtnLoading,setApprovalBtnLoading] = useState(false) const [memberValueEnum, setMemberValueEnum] = useState([]) const {accessData,state} = useGlobalContext() - const navigator = useNavigate() const openModal = async (type:'approval'|'view',entity:SubscribeApprovalTableListItem)=>{ message.loading($t(RESPONSE_TIPS.loading)) @@ -142,15 +139,6 @@ const AiServiceInsideApprovalList:FC = ()=>{ }, [query]); useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title:$t('订阅审核') - } - ]) getMemberList() manualReloadTable() }, [serviceId]); diff --git a/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublish.tsx b/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublish.tsx index b80b82b1..a492b3e4 100644 --- a/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublish.tsx +++ b/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublish.tsx @@ -2,13 +2,11 @@ import { Tabs } from "antd" import { useState, useEffect, FC, useMemo } from "react" import { Link, Outlet, useLocation, useNavigate } from "react-router-dom" -import { useBreadcrumb } from "@common/contexts/BreadcrumbContext" import { SYSTEM_PUBLISH_TAB_ITEMS } from "../../../const/system/const" import { $t } from "@common/locales" import { useGlobalContext } from "@common/contexts/GlobalStateContext" const AiServiceInsidePublic:FC = ()=>{ - const { setBreadcrumb } = useBreadcrumb() const query =new URLSearchParams(useLocation().search) const location = useLocation() const currentUrl = location.pathname @@ -25,18 +23,6 @@ const AiServiceInsidePublic:FC = ()=>{ setPageStatus(Number(query.get('status') ||0) as 0|1) }, [currentUrl]); - useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigateTo('/service/list') - }, - { - title:$t('发布') - } - ]) - }, []); - const tabItems = useMemo(()=>SYSTEM_PUBLISH_TAB_ITEMS?.map((x)=>({...x, label:$t(x.label as string) })),[state.language]) return ( <> diff --git a/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublishList.tsx b/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublishList.tsx index c3ec8573..09a17009 100644 --- a/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublishList.tsx +++ b/frontend/packages/core/src/pages/aiService/publish/AiServiceInsidePublishList.tsx @@ -9,7 +9,6 @@ import { PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN, PUBLISH_APPROVAL_VERSION_IN import { BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const"; import { SimpleMemberItem } from "@common/const/type.ts"; import { MemberTableListItem } from "../../../const/member/type"; -import { useBreadcrumb } from "@common/contexts/BreadcrumbContext"; import { useFetch } from "@common/hooks/http"; import WithPermission from "@common/components/aoplatform/WithPermission"; import { AiServicePublishReleaseItem } from "../../../const/system/type"; @@ -23,7 +22,6 @@ import { DrawerWithFooter } from "@common/components/aoplatform/DrawerWithFooter import { $t } from "@common/locales"; const AiServiceInsidePublicList:FC = ()=>{ - const { setBreadcrumb } = useBreadcrumb() const { modal,message } = App.useApp() const pageListRef = useRef(null); const [tableHttpReload, setTableHttpReload] = useState(true); @@ -45,7 +43,6 @@ const AiServiceInsidePublicList:FC = ()=>{ const [drawerData, setDrawerData] = useState({} as PublishTableListItem) const [drawerOkTitle, setDrawerOkTitle] = useState('确认') const [isOkToPublish, setIsOkToPublish] = useState(false) - const navigator = useNavigate() const getAiServicePublishList = (params?: ParamsType & { pageSize?: number | undefined; current?: number | undefined; @@ -351,15 +348,6 @@ const AiServiceInsidePublicList:FC = ()=>{ ] useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('发布') - } - ]) getMemberList() manualReloadTable() }, [serviceId]); diff --git a/frontend/packages/core/src/pages/policy/ServicePolicyLayout.tsx b/frontend/packages/core/src/pages/policy/ServicePolicyLayout.tsx index 3ac38da1..ed15ddb1 100644 --- a/frontend/packages/core/src/pages/policy/ServicePolicyLayout.tsx +++ b/frontend/packages/core/src/pages/policy/ServicePolicyLayout.tsx @@ -1,25 +1,13 @@ -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' import { useEffect } from 'react' import { Outlet, useLocation, useNavigate } from 'react-router-dom' -import { $t } from '@common/locales' export default function ServicePolicyLayout() { const location = useLocation() const pathName = location.pathname const navigator = useNavigate() - const { setBreadcrumb } = useBreadcrumb() useEffect(() => { const tmpPath = pathName.split('/') if (tmpPath[tmpPath.length - 1] === 'servicepolicy') { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('服务策略') - } - ]) navigator('datamasking/list') } }, [pathName]) diff --git a/frontend/packages/core/src/pages/policy/dataMasking/DataMaskingConfig.tsx b/frontend/packages/core/src/pages/policy/dataMasking/DataMaskingConfig.tsx index ec4aa016..efc778d3 100644 --- a/frontend/packages/core/src/pages/policy/dataMasking/DataMaskingConfig.tsx +++ b/frontend/packages/core/src/pages/policy/dataMasking/DataMaskingConfig.tsx @@ -80,19 +80,6 @@ const DataMaskingConfig = forwardRef((_,ref) => { }; useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title:$t('服务策略'), - onClick: () => navigator(serviceId ? `/service/${teamId}/aiInside/${serviceId}/servicepolicy` : '') - }, - { - title: policyId !== undefined ? $t('编辑服务策略') : $t('添加服务策略') - } - ]) if (policyId !== undefined) { setOnEdit(true); getPolicyInfo(); diff --git a/frontend/packages/core/src/pages/system/SystemConfig.tsx b/frontend/packages/core/src/pages/system/SystemConfig.tsx index 14426200..d12671d2 100644 --- a/frontend/packages/core/src/pages/system/SystemConfig.tsx +++ b/frontend/packages/core/src/pages/system/SystemConfig.tsx @@ -2,7 +2,6 @@ import { LoadingOutlined } from '@ant-design/icons' import WithPermission from '@common/components/aoplatform/WithPermission.tsx' import { BasicResponse, DELETE_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const.tsx' import { EntityItem, MemberItem, SimpleTeamItem } from '@common/const/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx' import { useFetch } from '@common/hooks/http.ts' import { $t } from '@common/locales/index.ts' @@ -49,7 +48,6 @@ const SystemConfig = forwardRef((_, ref) => { const { fetchData } = useFetch() const [teamOptionList, setTeamOptionList] = useState() const navigate = useNavigate() - const { setBreadcrumb } = useBreadcrumb() const { setSystemInfo } = useSystemContext() const [showClassify, setShowClassify] = useState(true) const [showAI, setShowAI] = useState(false) @@ -355,15 +353,6 @@ const SystemConfig = forwardRef((_, ref) => { if (serviceId !== undefined) { setOnEdit(true) getSystemInfo() - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigate('/service/list') - }, - { - title: $t('设置') - } - ]) } else { getProviderOptionList() setOnEdit(false) diff --git a/frontend/packages/core/src/pages/system/SystemInsideDocument.tsx b/frontend/packages/core/src/pages/system/SystemInsideDocument.tsx index 719bd653..cfa226f1 100644 --- a/frontend/packages/core/src/pages/system/SystemInsideDocument.tsx +++ b/frontend/packages/core/src/pages/system/SystemInsideDocument.tsx @@ -1,7 +1,6 @@ import WithPermission from '@common/components/aoplatform/WithPermission.tsx' import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const.tsx' import { EntityItem } from '@common/const/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' import { useFetch } from '@common/hooks/http.ts' import { $t } from '@common/locales' import { RouterParams } from '@core/components/aoplatform/RenderRoutes' @@ -10,7 +9,7 @@ import { App, Button } from 'antd' import hljs from 'highlight.js' import 'highlight.js/styles/default.css' import { useEffect, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' const ServiceInsideDocument = () => { const { message } = App.useApp() const [updater, setUpdater] = useState() @@ -19,8 +18,6 @@ const ServiceInsideDocument = () => { const [doc, setDoc] = useState() const { fetchData } = useFetch() const { serviceId, teamId } = useParams() - const { setBreadcrumb } = useBreadcrumb() - const navigator = useNavigate() const save = () => { fetchData< BasicResponse<{ @@ -88,15 +85,6 @@ const ServiceInsideDocument = () => { } useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('使用说明') - } - ]) getServiceDoc() }, []) diff --git a/frontend/packages/core/src/pages/system/SystemInsidePage.tsx b/frontend/packages/core/src/pages/system/SystemInsidePage.tsx index 041819b9..2f7c40a7 100644 --- a/frontend/packages/core/src/pages/system/SystemInsidePage.tsx +++ b/frontend/packages/core/src/pages/system/SystemInsidePage.tsx @@ -15,6 +15,7 @@ import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router- import { SystemConfigFieldType } from '../../const/system/type.ts' import { useSystemContext } from '../../contexts/SystemContext.tsx' import ServiceInfoCard from '@common/components/aoplatform/serviceInfoCard.tsx' +import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' const SystemInsidePage: FC = () => { const { message } = App.useApp() @@ -27,6 +28,7 @@ const SystemInsidePage: FC = () => { const [activeMenu, setActiveMenu] = useState() const navigateTo = useNavigate() const [showMenu, setShowMenu] = useState(false) + const { setBreadcrumb } = useBreadcrumb() const getSystemInfo = () => { fetchData>('service/info', { @@ -76,7 +78,7 @@ const SystemInsidePage: FC = () => { 'team.service.router.view' ), getItem( - {$t('API 文档')}, + {$t('API 路由文档')}, 'api', undefined, undefined, @@ -217,10 +219,19 @@ const SystemInsidePage: FC = () => { }, [accessData]) useEffect(() => { + setBreadcrumb([ + { + title: $t('服务'), + onClick: () => navigateTo('/service/list') + }, + { + title: systemInfo?.name || '' + } + ]) if (activeMenu && serviceId === currentUrl.split('/')[currentUrl.split('/').length - 1]) { navigateTo(`/service/${teamId}/inside/${serviceId}/${activeMenu}`) } - }, [activeMenu]) + }, [activeMenu, systemInfo, state.language]) useEffect(() => { serviceId && getSystemInfo() diff --git a/frontend/packages/core/src/pages/system/SystemInsideSubscriber.tsx b/frontend/packages/core/src/pages/system/SystemInsideSubscriber.tsx index 586e21f8..10aed7a0 100644 --- a/frontend/packages/core/src/pages/system/SystemInsideSubscriber.tsx +++ b/frontend/packages/core/src/pages/system/SystemInsideSubscriber.tsx @@ -11,7 +11,6 @@ import { STATUS_CODE } from '@common/const/const.tsx' import { SimpleMemberItem } from '@common/const/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx' import { useFetch } from '@common/hooks/http.ts' import { $t } from '@common/locales/index.ts' @@ -20,7 +19,7 @@ import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx' import { App, Form, TreeSelect } from 'antd' import { DefaultOptionType } from 'antd/es/cascader' import { FC, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' -import { Link, useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { SYSTEM_SUBSCRIBER_TABLE_COLUMNS } from '../../const/system/const.tsx' import { SimpleSystemItem, @@ -31,7 +30,6 @@ import { } from '../../const/system/type.ts' const SystemInsideSubscriber: FC = () => { - const { setBreadcrumb } = useBreadcrumb() const { modal, message } = App.useApp() const { fetchData } = useFetch() const { serviceId, teamId } = useParams() @@ -39,7 +37,6 @@ const SystemInsideSubscriber: FC = () => { const pageListRef = useRef(null) const [memberValueEnum, setMemberValueEnum] = useState([]) const { accessData, state } = useGlobalContext() - const navigator = useNavigate() const getSystemSubscriber = () => { return fetchData>( 'service/subscribers', @@ -162,15 +159,6 @@ const SystemInsideSubscriber: FC = () => { ] useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('订阅方管理') - } - ]) getMemberList() manualReloadTable() }, [serviceId]) diff --git a/frontend/packages/core/src/pages/system/SystemTopology.tsx b/frontend/packages/core/src/pages/system/SystemTopology.tsx index 8bfd47c9..d5cb7601 100644 --- a/frontend/packages/core/src/pages/system/SystemTopology.tsx +++ b/frontend/packages/core/src/pages/system/SystemTopology.tsx @@ -2,7 +2,6 @@ import { ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons' import G6, { EdgeConfig, Graph, NodeConfig } from '@antv/g6' import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { EntityItem } from '@common/const/type' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' import { useFetch } from '@common/hooks/http' import { $t } from '@common/locales' import { getNodeSpacing } from '@common/utils/systemRunning' @@ -10,7 +9,7 @@ import { RouterParams } from '@core/components/aoplatform/RenderRoutes' import { App, Button } from 'antd' import { debounce } from 'lodash-es' import { useCallback, useEffect, useRef, useState } from 'react' -import { Link, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { RELATIVE_PICTURE_NODE_FONTSIZE } from '../../const/system-running/const' import { GraphData } from '../../const/system-running/type' import { SYSTEM_TOPOLOGY_NODE_TYPE_COLOR_MAP } from '../../const/system/const' @@ -26,9 +25,7 @@ export default function SystemTopology() { const [graph, setGraph] = useState(null) const { fetchData } = useFetch() const { systemInfo } = useSystemContext() - const { setBreadcrumb } = useBreadcrumb() const [zoomNum, setZoomNum] = useState(1) - const navigate = useNavigate() const getNodeData = () => { @@ -105,15 +102,6 @@ export default function SystemTopology() { useEffect(() => { getNodeData() - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigate('/service/list') - }, - { - title: $t('调用拓扑图') - } - ]) }, [serviceId]) useEffect(() => { diff --git a/frontend/packages/core/src/pages/system/api/SystemInsideApiDocument.tsx b/frontend/packages/core/src/pages/system/api/SystemInsideApiDocument.tsx index 77f82539..814a496e 100644 --- a/frontend/packages/core/src/pages/system/api/SystemInsideApiDocument.tsx +++ b/frontend/packages/core/src/pages/system/api/SystemInsideApiDocument.tsx @@ -9,13 +9,12 @@ import { $t } from '@common/locales/index.ts' import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx' import { Button, Empty, Spin, Upload, message } from 'antd' import { forwardRef, useEffect, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { SystemApiDetail, SystemInsideApiDocumentHandle, SystemInsideApiDocumentProps } from '../../../const/system/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' const SystemInsideApiDocument = forwardRef< SystemInsideApiDocumentHandle, @@ -26,18 +25,7 @@ const SystemInsideApiDocument = forwardRef< const [apiDetail, setApiDetail] = useState() const [loading, setLoading] = useState(false) const [showEditor, setShowEditor] = useState(false) - const { setBreadcrumb } = useBreadcrumb() - const navigator = useNavigate() useEffect(() => { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title: $t('API 文档') - } - ]) getApiDetail() }, []) diff --git a/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx b/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx index 5ce1ecb4..ecf601d2 100644 --- a/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx @@ -26,7 +26,6 @@ import { SystemInsideRouterCreateHandle, SystemInsideRouterCreateProps } from '../../../const/system/type.ts' -import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx' const SystemInsideRouterCreate = forwardRef( (props, ref) => { @@ -39,14 +38,12 @@ const SystemInsideRouterCreate = forwardRef { return Promise.all([proxyRef.current?.validate?.(), form.validateFields()]).then(([, formValue]) => { const body = { ...formValue, - path: `${prefixForce ? apiPrefix + '/' : ''}${formValue.path.trim()}${formValue.pathMatch === 'prefix' ? '/*' : ''}`, + path: `${prefixForce ? apiPrefix + (!formValue.path?.trim() ? '': '/') : ''}${(formValue.path?.trim() || '')}${formValue.pathMatch === 'prefix' ? '/*' : ''}`, proxy: { ...formValue.proxy, path: formValue.proxy.path @@ -147,19 +144,6 @@ const SystemInsideRouterCreate = forwardRef { - setBreadcrumb([ - { - title: $t('服务'), - onClick: () => navigator('/service/list') - }, - { - title:$t('API'), - onClick: () => navigator(`/service/${teamId}/inside/${serviceId}/route`) - }, - { - title: routeId ? $t('编辑 API') : $t('添加 API') - } - ]) if (routeId) { getRouterConfig() } else { @@ -253,6 +237,14 @@ const SystemInsideRouterCreate = forwardRef + + className="flex-1" + label={$t('路由名称')} + name="name" + rules={[{ required: true, whitespace: true }]} + > + + label={$t('请求协议')} name="protocols" rules={[{ required: true }]}> + + label={$t('请求前缀')} name="url" rules={[{ required: true }]}> - - label={$t("HTTP 头部")} - name="headers" - > + label={$t('HTTP 头部')} name="headers"> configFields={PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS} />
- + @@ -84,7 +111,7 @@ const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
- ); + ) } -export default DataLogSettingEdit; \ No newline at end of file +export default DataLogSettingEdit diff --git a/frontend/packages/core/src/pages/partitions/PartitionInsideDashboardSetting.tsx b/frontend/packages/core/src/pages/partitions/PartitionInsideDashboardSetting.tsx index 6e243094..b732c6ab 100644 --- a/frontend/packages/core/src/pages/partitions/PartitionInsideDashboardSetting.tsx +++ b/frontend/packages/core/src/pages/partitions/PartitionInsideDashboardSetting.tsx @@ -161,7 +161,7 @@ const PartitionInsideDashboardSetting: FC = () => { className="overflow-hidden mt-[30px] w-full max-h-full flex flex-col justify-between" title={
- {$t('数据日志')} + {$t('请求日志')} {!dataLogLoading && !dataLogData && {$t('未配置')}}
} @@ -220,6 +220,11 @@ export function DataLogConfigPreview(x: PartitionDataLogConfigFieldType) { return (
+ + {$t('数据源')}: + {/* 先写死,或许会有选择列表,但现在可以不用 */} + Loki + {$t('请求前缀')}: {x?.url} diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index bfe7a00b..f9d82b56 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -9,6 +9,7 @@ type AreaChartInfo = { data: number[] max: string min: string + showXAxis?: boolean } type ServiceAreaCharProps = { @@ -24,27 +25,65 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha const option = { tooltip: { trigger: 'axis', - position: function (pt) { - return [pt[0], '10%'] + formatter: function (value: any) { + // 如果是数组,取第一个参数的name + const param = Array.isArray(value) ? value[0] : value + console.log('params==', param) + let tooltipContent = `
` + const marker = `` + tooltipContent += `
+
${marker}
${param.name}
${param.value}
+
` + tooltipContent += '
' + return tooltipContent } }, - title: { - show: false - }, + title: [ + { + text: '{titleStyle|' + $t(dataInfo.title) + '}\n\n{valueStyle|' + dataInfo.value + '}', + left: '2%', + top: '0', + textStyle: { + rich: { + titleStyle: { + fontSize: 14, + color: '#999', + fontWeight: 'normal', + lineHeight: 20 + }, + valueStyle: { + fontSize: 32, + color: '#101010', + fontWeight: 500, + lineHeight: 40 + } + } + } + } + ], toolbox: { show: false }, grid: { - left: '5%', + left: '3%', right: '3%', - bottom: '5%', - top: '100px', + bottom: '0%', + top: '110px', containLabel: true }, xAxis: { type: 'category', boundaryGap: false, - data: dataInfo.date + data: dataInfo.date, + axisTick: { + show: false + }, + axisLine: { + lineStyle: { + color: '#ccc' + } + }, + show: false }, yAxis: { type: 'value', @@ -59,7 +98,51 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha show: false } }, - dataZoom: [], + // 添加数据缩放组件,实现鼠标放大缩小,后续可能需要 + // dataZoom: [ + // { + // type: 'inside', // 内置的数据区域缩放组件(使用鼠标滚轮缩放) + // xAxisIndex: 0, // 设置缩放作用在第一个x轴 + // filterMode: 'filter', + // start: 0, + // end: 100 + // }, + // { + // type: 'slider', // 滑动条型数据区域缩放组件 + // xAxisIndex: 0, + // filterMode: 'filter', + // height: 20, + // bottom: 0, + // start: 0, + // end: 100, + // handleIcon: + // 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + // handleSize: '80%', + // handleStyle: { + // color: '#fff', + // shadowBlur: 3, + // shadowColor: 'rgba(0, 0, 0, 0.6)', + // shadowOffsetX: 2, + // shadowOffsetY: 2 + // }, + // show: false // 默认隐藏底部的滑动条,可以改为 true 显示 + // } + // ], + // 添加空状态提示 + graphic: !dataInfo.data.length + ? [ + { + type: 'text', + left: 'center', + top: 'middle', + style: { + text: $t('暂无数据'), + fontSize: 14, + fill: '#999' + } + } + ] + : [], series: [ { name: dataInfo.title, @@ -94,23 +177,30 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha } setOption(option) } + // 使用深度监听来确保图表数据更新 useEffect(() => { if (!dataInfo) return - setChartOption(dataInfo) - }, [dataInfo]) + + // 直接获取 ECharts 实例并设置选项 + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + // 清除已有的图表 + echartsInstance.clear() + // 重新设置选项 + setChartOption(dataInfo) + } + }, [dataInfo, JSON.stringify(dataInfo)]) return (
-
{$t(dataInfo?.title || '')}
-
- {dataInfo?.value} -
+
+
- +
{dataInfo?.max}
- +
{dataInfo?.min}
diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index d7ce6294..4b1b4226 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -12,6 +12,7 @@ export type BarChartInfo = { color: string value: number[] }[] + showXAxis?: boolean } type ServiceBarCharProps = { @@ -20,7 +21,6 @@ type ServiceBarCharProps = { height?: number } - const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) @@ -33,6 +33,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const setChartOption = (dataInfo: BarChartInfo) => { const isNumberArray = typeof dataInfo.data[0] !== 'object' const legendData = isNumberArray ? [dataInfo.title] : dataInfo.data.map((item) => item.name) + const hasData = dataInfo.data && dataInfo.data.length > 0 const tooltipFormatter = (params: { name: string; color: string; seriesIndex?: number }) => { let tooltipContent = `
${isNumberArray ? '' : params.name}
` @@ -63,8 +64,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const option: EChartsOption = { title: [ { - text: '{titleStyle|' + $t(dataInfo.title) + '}\n{valueStyle|' + dataInfo.value + '}', - left: '4%', + text: '{titleStyle|' + $t(dataInfo.title) + '}\n\n{valueStyle|' + dataInfo.value + '}', + left: '2%', top: '0', textStyle: { rich: { @@ -75,8 +76,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr lineHeight: 20 }, valueStyle: { - fontSize: 25, - color: '#000', + fontSize: 32, + color: '#101010', fontWeight: 500, lineHeight: 40 } @@ -85,10 +86,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr } ], grid: { - left: '5%', + left: '3%', right: '3%', - bottom: '5%', - top: '100px', + bottom: '0%', + top: '110px', containLabel: true }, tooltip: { @@ -124,12 +125,14 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr lineStyle: { color: '#ccc' } - } + }, + show: false }, yAxis: { type: 'value', name: '', min: 0, + minInterval: 1, splitLine: { lineStyle: { type: 'dashed', @@ -140,6 +143,50 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr formatter: '{value}' } }, + // 添加数据缩放组件,实现鼠标放大缩小,后续可能需要 + // dataZoom: [ + // { + // type: 'inside', // 内置的数据区域缩放组件(使用鼠标滚轮缩放) + // xAxisIndex: 0, // 设置缩放作用在第一个x轴 + // filterMode: 'filter', + // start: 0, + // end: 100 + // }, + // { + // type: 'slider', // 滑动条型数据区域缩放组件 + // xAxisIndex: 0, + // filterMode: 'filter', + // height: 20, + // bottom: 0, + // start: 0, + // end: 100, + // handleIcon: 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + // handleSize: '80%', + // handleStyle: { + // color: '#fff', + // shadowBlur: 3, + // shadowColor: 'rgba(0, 0, 0, 0.6)', + // shadowOffsetX: 2, + // shadowOffsetY: 2 + // }, + // show: false // 默认隐藏底部的滑动条,可以改为 true 显示 + // } + // ], + // 添加空状态提示 + graphic: !hasData + ? [ + { + type: 'text', + left: 'center', + top: 'middle', + style: { + text: $t('暂无数据'), + fontSize: 14, + fill: '#999' + } + } + ] + : [], series: isNumberArray ? [ { @@ -171,10 +218,19 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr setOption(option) } + // 使用深度监听来确保图表数据更新 useEffect(() => { if (!dataInfo) return - setChartOption(dataInfo) - }, [dataInfo]) + + // 直接获取 ECharts 实例并设置选项 + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + // 清除已有的图表 + echartsInstance.clear() + // 重新设置选项 + setChartOption(dataInfo) + } + }, [dataInfo, JSON.stringify(dataInfo)]) return (
diff --git a/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx index 9f4454a2..d51f4de9 100644 --- a/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx @@ -18,33 +18,30 @@ const Indicator = ({ indicatorInfo }: { indicatorInfo: any }) => { /** 设置服务指标 */ const setIndicatorList = () => { + const side = indicatorInfo?.serviceKind === 'ai' ? 'aiInside' : 'inside' setIndicator([ { title: indicatorInfo?.enableMcp ? 'APIs / Tools' : 'APIs', - link: `/serviceHub/detail/${indicatorInfo?.serviceId}`, + link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/route`, content: indicatorInfo?.apiNum ?? 0 }, { title: $t('订阅数量'), - link: `/consumer/list/${indicatorInfo?.teamId}`, + link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/subscriber`, content: indicatorInfo?.subscriberNum ?? 0 }, { title: 'MCP', + link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/setting`, content: ( <> - {/* green */} From 48f46ec4c319f1cda86ef2cefbb8e105b616e815 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 29 Apr 2025 17:32:49 +0800 Subject: [PATCH 11/29] feature/1.8-Improve system observability --- .../packages/common/src/locales/keyHashMap.json | 13 +++++++++---- .../packages/common/src/locales/scan/en-US.json | 6 +++++- .../packages/common/src/locales/scan/ja-JP.json | 6 +++++- .../packages/common/src/locales/scan/zh-CN.json | 6 +++++- .../packages/common/src/locales/scan/zh-TW.json | 6 +++++- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/frontend/packages/common/src/locales/keyHashMap.json b/frontend/packages/common/src/locales/keyHashMap.json index f796c540..59bf3985 100644 --- a/frontend/packages/common/src/locales/keyHashMap.json +++ b/frontend/packages/common/src/locales/keyHashMap.json @@ -246,6 +246,7 @@ "调用地址": "K2f5fdf5e", "消费者 IP": "K1bc5e0a3", "鉴权名称": "K6f39ea21", + "日志输出": "K3c722abd", "暂无操作权限,请联系管理员分配。": "K23fda291", "微信小程序": "K4618cb0a", "获取文件,需填路径": "Ka854f511", @@ -355,7 +356,7 @@ "保存": "Kabfe9512", "服务": "Kb58e0c3f", "API 路由": "K51d1eb5d", - "API 路由文档": "K1639a17a", + "API 文档": "Ka2b6d281", "使用说明": "Kdefa9caa", "服务策略": "K52f72551", "发布": "K36856e71", @@ -553,8 +554,9 @@ "访客模式": "K192b3e38", "您可通过访客模式查看所有页面和功能,但是无法编辑数据。访客模式仅用于了解产品功能,您可以在正式产品中关闭该功能。": "K91aa4801", "Version (0)-(1)": "K480045ce", - "日志配置": "Kadee8e49", + "日志输出设置": "K74a5fbc0", "提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。": "K2724314b", + "日志配置": "Kadee8e49", "MCP 配置": "K6e9c928f", "Open API 文档": "Kb6d0eb39", "AI 代理集成": "Ke6908f16", @@ -621,7 +623,7 @@ "数据源": "K8fa58214", "设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。": "Kdbafd6f9", "统计图表": "K1358acf", - "数据日志": "K17dc3a62", + "请求日志": "Kc8bf447", "地址(IP:端口)": "K62dabdf6", "组织(Organization)": "K2db12335", "添加策略": "K34d0d409", @@ -666,6 +668,10 @@ "IP": "Kb09b747", "通过系统级别的 API Key 来调用": "K2eacb44f", "日志详情": "K764bca7c", + "暂无数据": "Kf8525cf2", + "输入 Token": "K33bc1ad1", + "输出 Token": "Ke00ff18b", + "总 Token": "K81140e5b", "订阅数量": "Ke04bc00d", "已开启": "K1b97ae0a", "开启 MCP": "K19ec733b", @@ -855,7 +861,6 @@ "请确认是否取消订阅申请?": "K1856c229", "搜索服务": "K66ea2f0", "审核中": "K8adf7f8b", - "API 文档": "Ka2b6d281", "添加消费者": "K84c4dc71", "暂无消费者描述": "Kc3b7bfa8", "创建并管理自己的消费者实体,每个消费者可以订阅多个API服务,确保在调用之前已获得相应权限。你可以为消费者生成 API 密钥等鉴权方式,用于安全地调用 API 服务": "K5c4e2865", diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 2ff874a1..386c0919 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -960,5 +960,9 @@ "K1639a17a": "API Routes Docs", "K33bc1ad1": "Input Token", "Ke00ff18b": "Output Token", - "K81140e5b": "Total Token" + "K81140e5b": "Total Token", + "K3c722abd": "Log Output", + "K74a5fbc0": "Log Output Settings", + "Kc8bf447": "Request Log", + "Kf8525cf2": "No Data" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index c475c4a1..379bdb04 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -982,5 +982,9 @@ "K1639a17a": "APIルートのドキュメント", "K33bc1ad1": "入力トークン", "Ke00ff18b": "出力トークン", - "K81140e5b": "合計トークン" + "K81140e5b": "合計トークン", + "K3c722abd": "ログ出力", + "K74a5fbc0": "ログ出力設定", + "Kc8bf447": "リクエストログ", + "Kf8525cf2": "データがありません" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index ea4f13e4..85e21b30 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -911,5 +911,9 @@ "K1639a17a": "API 路由文档", "K33bc1ad1": "输入 Token", "Ke00ff18b": "输出 Token", - "K81140e5b": "总 Token" + "K81140e5b": "总 Token", + "K3c722abd": "日志输出", + "K74a5fbc0": "日志输出设置", + "Kc8bf447": "请求日志", + "Kf8525cf2": "暂无数据" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index 317c3eed..1d4cd1df 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -982,5 +982,9 @@ "K1639a17a": "API 路由文件", "K33bc1ad1": "輸入 Token", "Ke00ff18b": "輸出 Token", - "K81140e5b": "總計 Token" + "K81140e5b": "總計 Token", + "K3c722abd": "日誌輸出", + "K74a5fbc0": "日誌輸出設定", + "Kc8bf447": "請求日誌", + "Kf8525cf2": "暫無資料" } From c45cf6aaffe125cef497c5be58d0d22dfc66b053 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 29 Apr 2025 18:23:51 +0800 Subject: [PATCH 12/29] feature/1.8-Improve system observability --- .../packages/core/src/const/system/const.tsx | 2 +- .../core/src/pages/serviceLogs/LogDetail.tsx | 68 +-------- .../src/pages/serviceLogs/ServiceLogs.tsx | 136 ++++++++---------- .../charts/ServiceAreaChart.tsx | 19 +++ .../serviceOverview/charts/ServiceBarChar.tsx | 19 +++ .../rankingList/RankingList.tsx | 6 +- 6 files changed, 105 insertions(+), 145 deletions(-) diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index cabbf89a..45935f93 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -599,7 +599,7 @@ export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ }, { title: '消费者', - dataIndex: ['consumers', 'name'], + dataIndex: ['consumer', 'name'], ellipsis: true }, { diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx index eb393321..a3b222ad 100644 --- a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -260,46 +260,11 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro fetchData>('service/log/ai', { method: 'GET', eoParams: { log: selectedRow?.id, service: serviceId, team: teamId }, - eoTransformKeys: ['is_system_consumer', 'log_time'], - eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + eoTransformKeys: ['is_system_consumer', 'log_time'] }).then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { - // const result = data.log - const result = { - id: '123', - api: { - id: '222', - name: 'api222' - }, - logTime: '2023-01-01 00:00:00', - consumer: { - id: '333', - name: 'consumers333' - }, - isSystemConsumer: false, - status: '200', - provider: { - id: '444', - name: 'provider444' - }, - model: 'model1', - ip: '1.1.1.1', - request: { - header: - '{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - origin: '123', - token: 0 - }, - response: { - header: - '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - origin: '312', - token: '333' - } - } + const result = data.log getAIServiceDescriptionItemsList({ time: result.logTime, api: result.api.name, @@ -329,36 +294,11 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro fetchData>('service/log/rest', { method: 'GET', eoParams: { log: selectedRow?.id, service: serviceId, team: teamId }, - eoTransformKeys: ['is_system_consumer', 'log_time'], - eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + eoTransformKeys: ['is_system_consumer', 'log_time'] }).then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { - const result = { - id: '123', - api: { - id: '222', - name: 'api222' - }, - logTime: '2023-01-01 00:00:00', - consumer: { - id: '333', - name: 'consumers333' - }, - isSystemConsumer: true, - status: '200', - ip: '1.1.1.1', - request: { - header: - '{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - origin: '123' - }, - response: { - header: - '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}', - origin: '312' - } - } + const result = data.log getRestServiceDescriptionItemsList({ time: result.logTime, api: result.api.name, diff --git a/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx index 0fc440ed..e51ea041 100644 --- a/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx @@ -11,7 +11,7 @@ import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { useFetch } from '@common/hooks/http' import LogDetail, { HttpStatusColor } from './LogDetail' import { useParams } from 'react-router-dom' -import { ActionType } from '@ant-design/pro-components' +import { ActionType, ParamsType } from '@ant-design/pro-components' import { getTime } from '@dashboard/utils/dashboard' export type LogItem = { @@ -66,9 +66,7 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' if (x.dataIndex === 'status') { x.render = (text: any, record: any) => ( <> -
- {renderStatusWithColor(record.status)} -
+
{renderStatusWithColor(record.status)}
) } @@ -79,73 +77,62 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' }) }, [state.language]) - /** - * 根据状态码返回对应颜色的文本 - * @param status 状态 - * @returns - */ - const renderStatusWithColor = (status: string | number) => { - // 获取状态码首位数字 - const firstDigit = status.toString().charAt(0) - let color = '' - switch (firstDigit) { - case '2': - color = HttpStatusColor.SUCCESS - break - case '4': - color = HttpStatusColor.CLIENT_ERROR - break - case '5': - color = HttpStatusColor.SERVER_ERROR - break - default: - break - } - return color ? {status} : status + /** + * 根据状态码返回对应颜色的文本 + * @param status 状态 + * @returns + */ + const renderStatusWithColor = (status: string | number) => { + // 获取状态码首位数字 + const firstDigit = status.toString().charAt(0) + let color = '' + switch (firstDigit) { + case '2': + color = HttpStatusColor.SUCCESS + break + case '4': + color = HttpStatusColor.CLIENT_ERROR + break + case '5': + color = HttpStatusColor.SERVER_ERROR + break + default: + break } + return color ? {status} : status + } /** * 获取 AI 列表数据 * @param dataType * @returns */ - const getAiServiceLogList = () => { + const getAiServiceLogList = ( + params: ParamsType & { + pageSize?: number | undefined + current?: number | undefined + keyword?: string | undefined + } + ) => { return fetchData>(`service/logs/ai`, { method: 'GET', eoParams: { service: serviceId, team: teamId, start: timeRange?.start, - end: timeRange?.end + end: timeRange?.end, + page: params?.current, + page_size:params?.pageSize }, - eoTransformKeys: ['log_time', 'response_time', 'token_per_second'], - eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + eoTransformKeys: ['log_time', 'response_time', 'token_per_second'] }) .then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { // 保存数据 return { - data: [ - { - id: '123123', - api: { - id: '444', - name: 'api1' - }, - ip: '127.0.0.1', - status: 200, - logTime: '2023-01-01 00:00:00', - token: 123, - consumers: { - id: '333', - name: 'consumers333' - }, - model: 'GPT444', - tokenPerSecond: '123m/s' - } - ], - total: 1, + data: data.logs, + total: data.total, success: true } } else { @@ -162,44 +149,33 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' * @param dataType * @returns */ - const getRestServiceLogList = () => { + const getRestServiceLogList = ( + params: ParamsType & { + pageSize?: number | undefined + current?: number | undefined + keyword?: string | undefined + } + ) => { + console.log('params===', params) return fetchData>(`service/logs/rest`, { method: 'GET', eoParams: { service: serviceId, team: teamId, start: timeRange?.start, - end: timeRange?.end + end: timeRange?.end, + page: params?.current, + page_size:params?.pageSize }, - eoTransformKeys: ['log_time', 'response_time', 'token_per_second'], - eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/' + eoTransformKeys: ['log_time', 'response_time', 'token_per_second'] }) .then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { - const data = [] - for (let i = 0; i < 100; i++) { - data.push({ - id: '123123' + i, - api: { - id: '444' + i, - name: 'api1' + i - }, - ip: '127.0.0.1', - status: 200, - logTime: '2023-01-01 00:00:00', - responseTime: '1111-01-01 00:00:00', - traffic: '123', - consumers: { - id: '123' + i, - name: 'consumers222' + i - } - }) - } // 保存数据 return { - data: data, - total: data.length, + data: data.logs, + total: data.total, success: true } } else { @@ -260,7 +236,13 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' id={`${serviceType}_logs`} columns={[...columns]} minVirtualHeight={430} - request={async () => (serviceType === 'aiService' ? getAiServiceLogList() : getRestServiceLogList())} + request={async ( + params: ParamsType & { + pageSize?: number | undefined + current?: number | undefined + keyword?: string | undefined + } + ) => (serviceType === 'aiService' ? getAiServiceLogList(params) : getRestServiceLogList(params))} onRowClick={(row: LogItem) => handleRowClick(row)} />
diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index f9d82b56..b3c33a44 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -190,6 +190,25 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha setChartOption(dataInfo) } }, [dataInfo, JSON.stringify(dataInfo)]) + + // 添加窗口大小变化监听,实现自适应 + useEffect(() => { + // 定义resize处理函数 + const handleResize = () => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + echartsInstance.resize() + } + } + + // 添加监听 + window.addEventListener('resize', handleResize) + + // 组件卸载时移除监听 + return () => { + window.removeEventListener('resize', handleResize) + } + }, []) return (
diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index 4b1b4226..b42e2714 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -231,6 +231,25 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr setChartOption(dataInfo) } }, [dataInfo, JSON.stringify(dataInfo)]) + + // 添加窗口大小变化监听,实现自适应 + useEffect(() => { + // 定义resize处理函数 + const handleResize = () => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + echartsInstance.resize() + } + } + + // 添加监听 + window.addEventListener('resize', handleResize) + + // 组件卸载时移除监听 + return () => { + window.removeEventListener('resize', handleResize) + } + }, []) return (
diff --git a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx index 6c1cb559..f0f8776e 100644 --- a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx @@ -59,11 +59,11 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL }) } return ( -
+
{Object.keys(topRankingList)?.map((item: any, index: number) => ( 0 ? 'ml-[10px]' : ''}`} + className={`flex-1 min-w-[300px] h-fit rounded-[10px] ${index > 0 ? 'ml-[10px] md:ml-[10px] sm:ml-0 sm:mt-[10px]' : ''}`} classNames={{ body: 'p-[15px]' }} @@ -75,7 +75,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL id={item} columns={[...columns]} minVirtualHeight={430} - noScroll + noScroll={true} request={() => getTableData(item)} showPagination={false} tableClass="ranking-list" From 159bcc7aa613137a63faeae37dc320766de3b625 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 29 Apr 2025 19:06:28 +0800 Subject: [PATCH 13/29] feature/1.8-Improve system observability --- .../src/const/charts/apipark-chart-palette.js | 431 ++++++++++++++++++ .../const/charts/apipark-chart-palette.json | 409 +++++++++++++++++ .../common/src/const/charts/initChartTheme.ts | 12 + .../packages/common/src/const/charts/theme.ts | 11 + frontend/packages/core/src/App.tsx | 4 + .../charts/ServiceAreaChart.tsx | 11 +- .../serviceOverview/charts/ServiceBarChar.tsx | 27 +- .../rankingList/RankingList.tsx | 6 +- .../pages/serviceOverview/serviceOverview.tsx | 4 +- .../dashboard/src/pages/DashboardTotal.tsx | 4 +- 10 files changed, 901 insertions(+), 18 deletions(-) create mode 100644 frontend/packages/common/src/const/charts/apipark-chart-palette.js create mode 100644 frontend/packages/common/src/const/charts/apipark-chart-palette.json create mode 100644 frontend/packages/common/src/const/charts/initChartTheme.ts create mode 100644 frontend/packages/common/src/const/charts/theme.ts diff --git a/frontend/packages/common/src/const/charts/apipark-chart-palette.js b/frontend/packages/common/src/const/charts/apipark-chart-palette.js new file mode 100644 index 00000000..8a01a55b --- /dev/null +++ b/frontend/packages/common/src/const/charts/apipark-chart-palette.js @@ -0,0 +1,431 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports', 'echarts'], factory); + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + factory(exports, require('echarts')); + } else { + // Browser globals + factory({}, root.echarts); + } +}(this, function (exports, echarts) { + var log = function (msg) { + if (typeof console !== 'undefined') { + console && console.error && console.error(msg); + } + }; + if (!echarts) { + log('ECharts is not Loaded'); + return; + } + echarts.registerTheme('apipark chart palette', { + "color": [ + "#4429e6", + "#fd6280", + "#28dbe2", + "#ffc404", + "#b92325", + "#1b9f17", + "#fe8705", + "#97b552", + "#95706d", + "#dc69aa", + "#07a2a4", + "#9a7fd1", + "#588dd5", + "#f5994e", + "#333333" + ], + "backgroundColor": "rgba(0,0,0,0)", + "textStyle": {}, + "title": { + "textStyle": { + "color": "#333333" + }, + "subtextStyle": { + "color": "#999999" + } + }, + "line": { + "itemStyle": { + "borderWidth": "2" + }, + "lineStyle": { + "width": "2" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true + }, + "radar": { + "itemStyle": { + "borderWidth": "2" + }, + "lineStyle": { + "width": "2" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true + }, + "bar": { + "itemStyle": { + "barBorderWidth": "2", + "barBorderColor": "rgba(255,255,255,0.3)" + } + }, + "pie": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "scatter": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "boxplot": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "parallel": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "sankey": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "funnel": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "gauge": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "candlestick": { + "itemStyle": { + "color": "#d87a80", + "color0": "#2ec7c9", + "borderColor": "#d87a80", + "borderColor0": "#2ec7c9", + "borderWidth": 1 + } + }, + "graph": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + }, + "lineStyle": { + "width": 1, + "color": "#aaaaaa" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true, + "color": [ + "#4429e6", + "#fd6280", + "#28dbe2", + "#ffc404", + "#b92325", + "#1b9f17", + "#fe8705", + "#97b552", + "#95706d", + "#dc69aa", + "#07a2a4", + "#9a7fd1", + "#588dd5", + "#f5994e", + "#333333" + ], + "label": { + "color": "#fefefe" + } + }, + "map": { + "itemStyle": { + "areaColor": "#dddddd", + "borderColor": "#eeeeee", + "borderWidth": 0.5 + }, + "label": { + "color": "#d87a80" + }, + "emphasis": { + "itemStyle": { + "areaColor": "rgba(254,153,78,1)", + "borderColor": "#444", + "borderWidth": 1 + }, + "label": { + "color": "rgb(100,0,0)" + } + } + }, + "geo": { + "itemStyle": { + "areaColor": "#dddddd", + "borderColor": "#eeeeee", + "borderWidth": 0.5 + }, + "label": { + "color": "#d87a80" + }, + "emphasis": { + "itemStyle": { + "areaColor": "rgba(254,153,78,1)", + "borderColor": "#444", + "borderWidth": 1 + }, + "label": { + "color": "rgb(100,0,0)" + } + } + }, + "categoryAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333333" + }, + "splitLine": { + "show": false, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": false, + "areaStyle": { + "color": [ + "rgba(250,250,250,0.3)", + "rgba(200,200,200,0.3)" + ] + } + } + }, + "valueAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": true, + "areaStyle": { + "color": [ + "#ffffff", + "rgba(0,0,0,0.02)" + ] + } + } + }, + "logAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": true, + "areaStyle": { + "color": [ + "#ffffff", + "rgba(0,0,0,0.02)" + ] + } + } + }, + "timeAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": false, + "areaStyle": { + "color": [ + "rgba(250,250,250,0.3)", + "rgba(200,200,200,0.3)" + ] + } + } + }, + "toolbox": { + "iconStyle": { + "borderColor": "#000000" + }, + "emphasis": { + "iconStyle": { + "borderColor": "#000000" + } + } + }, + "legend": { + "textStyle": { + "color": "#333333" + } + }, + "tooltip": { + "axisPointer": { + "lineStyle": { + "color": "rgba(0,0,0,0.3)", + "width": "1" + }, + "crossStyle": { + "color": "rgba(0,0,0,0.3)", + "width": "1" + } + } + }, + "timeline": { + "lineStyle": { + "color": "#008acd", + "width": 1 + }, + "itemStyle": { + "color": "#008acd", + "borderWidth": 1 + }, + "controlStyle": { + "color": "#008acd", + "borderColor": "#008acd", + "borderWidth": 0.5 + }, + "checkpointStyle": { + "color": "#2ec7c9", + "borderColor": "#2ec7c9" + }, + "label": { + "color": "#008acd" + }, + "emphasis": { + "itemStyle": { + "color": "#a9334c" + }, + "controlStyle": { + "color": "#008acd", + "borderColor": "#008acd", + "borderWidth": 0.5 + }, + "label": { + "color": "#008acd" + } + } + }, + "visualMap": { + "color": [ + "#ffffff", + "#4429e6" + ] + }, + "dataZoom": { + "backgroundColor": "rgba(47,69,84,0)", + "dataBackgroundColor": "#efefff", + "fillerColor": "rgba(182,162,222,0.2)", + "handleColor": "#008acd", + "handleSize": "100%", + "textStyle": { + "color": "#333333" + } + }, + "markPoint": { + "label": { + "color": "#fefefe" + }, + "emphasis": { + "label": { + "color": "#fefefe" + } + } + } + }); +})); diff --git a/frontend/packages/common/src/const/charts/apipark-chart-palette.json b/frontend/packages/common/src/const/charts/apipark-chart-palette.json new file mode 100644 index 00000000..d3afdf16 --- /dev/null +++ b/frontend/packages/common/src/const/charts/apipark-chart-palette.json @@ -0,0 +1,409 @@ +{ + "color": [ + "#4429e6", + "#fd6280", + "#28dbe2", + "#ffc404", + "#b92325", + "#1b9f17", + "#fe8705", + "#97b552", + "#95706d", + "#dc69aa", + "#07a2a4", + "#9a7fd1", + "#588dd5", + "#f5994e", + "#333333" + ], + "backgroundColor": "rgba(0,0,0,0)", + "textStyle": {}, + "title": { + "textStyle": { + "color": "#333333" + }, + "subtextStyle": { + "color": "#999999" + } + }, + "line": { + "itemStyle": { + "borderWidth": "2" + }, + "lineStyle": { + "width": "2" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true + }, + "radar": { + "itemStyle": { + "borderWidth": "2" + }, + "lineStyle": { + "width": "2" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true + }, + "bar": { + "itemStyle": { + "barBorderWidth": "2", + "barBorderColor": "rgba(255,255,255,0.3)" + } + }, + "pie": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "scatter": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "boxplot": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "parallel": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "sankey": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "funnel": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "gauge": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + } + }, + "candlestick": { + "itemStyle": { + "color": "#d87a80", + "color0": "#2ec7c9", + "borderColor": "#d87a80", + "borderColor0": "#2ec7c9", + "borderWidth": 1 + } + }, + "graph": { + "itemStyle": { + "borderWidth": "2", + "borderColor": "rgba(255,255,255,0.3)" + }, + "lineStyle": { + "width": 1, + "color": "#aaaaaa" + }, + "symbolSize": "5", + "symbol": "circle", + "smooth": true, + "color": [ + "#4429e6", + "#fd6280", + "#28dbe2", + "#ffc404", + "#b92325", + "#1b9f17", + "#fe8705", + "#97b552", + "#95706d", + "#dc69aa", + "#07a2a4", + "#9a7fd1", + "#588dd5", + "#f5994e", + "#333333" + ], + "label": { + "color": "#fefefe" + } + }, + "map": { + "itemStyle": { + "areaColor": "#dddddd", + "borderColor": "#eeeeee", + "borderWidth": 0.5 + }, + "label": { + "color": "#d87a80" + }, + "emphasis": { + "itemStyle": { + "areaColor": "rgba(254,153,78,1)", + "borderColor": "#444", + "borderWidth": 1 + }, + "label": { + "color": "rgb(100,0,0)" + } + } + }, + "geo": { + "itemStyle": { + "areaColor": "#dddddd", + "borderColor": "#eeeeee", + "borderWidth": 0.5 + }, + "label": { + "color": "#d87a80" + }, + "emphasis": { + "itemStyle": { + "areaColor": "rgba(254,153,78,1)", + "borderColor": "#444", + "borderWidth": 1 + }, + "label": { + "color": "rgb(100,0,0)" + } + } + }, + "categoryAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333333" + }, + "splitLine": { + "show": false, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": false, + "areaStyle": { + "color": [ + "rgba(250,250,250,0.3)", + "rgba(200,200,200,0.3)" + ] + } + } + }, + "valueAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": true, + "areaStyle": { + "color": [ + "#ffffff", + "rgba(0,0,0,0.02)" + ] + } + } + }, + "logAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": true, + "areaStyle": { + "color": [ + "#ffffff", + "rgba(0,0,0,0.02)" + ] + } + } + }, + "timeAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisTick": { + "show": true, + "lineStyle": { + "color": "rgba(0,0,0,0.1)" + } + }, + "axisLabel": { + "show": true, + "color": "#333" + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": false, + "areaStyle": { + "color": [ + "rgba(250,250,250,0.3)", + "rgba(200,200,200,0.3)" + ] + } + } + }, + "toolbox": { + "iconStyle": { + "borderColor": "#000000" + }, + "emphasis": { + "iconStyle": { + "borderColor": "#000000" + } + } + }, + "legend": { + "textStyle": { + "color": "#333333" + } + }, + "tooltip": { + "axisPointer": { + "lineStyle": { + "color": "rgba(0,0,0,0.3)", + "width": "1" + }, + "crossStyle": { + "color": "rgba(0,0,0,0.3)", + "width": "1" + } + } + }, + "timeline": { + "lineStyle": { + "color": "#008acd", + "width": 1 + }, + "itemStyle": { + "color": "#008acd", + "borderWidth": 1 + }, + "controlStyle": { + "color": "#008acd", + "borderColor": "#008acd", + "borderWidth": 0.5 + }, + "checkpointStyle": { + "color": "#2ec7c9", + "borderColor": "#2ec7c9" + }, + "label": { + "color": "#008acd" + }, + "emphasis": { + "itemStyle": { + "color": "#a9334c" + }, + "controlStyle": { + "color": "#008acd", + "borderColor": "#008acd", + "borderWidth": 0.5 + }, + "label": { + "color": "#008acd" + } + } + }, + "visualMap": { + "color": [ + "#ffffff", + "#4429e6" + ] + }, + "dataZoom": { + "backgroundColor": "rgba(47,69,84,0)", + "dataBackgroundColor": "#efefff", + "fillerColor": "rgba(182,162,222,0.2)", + "handleColor": "#008acd", + "handleSize": "100%", + "textStyle": { + "color": "#333333" + } + }, + "markPoint": { + "label": { + "color": "#fefefe" + }, + "emphasis": { + "label": { + "color": "#fefefe" + } + } + } +} \ No newline at end of file diff --git a/frontend/packages/common/src/const/charts/initChartTheme.ts b/frontend/packages/common/src/const/charts/initChartTheme.ts new file mode 100644 index 00000000..1a26f2f9 --- /dev/null +++ b/frontend/packages/common/src/const/charts/initChartTheme.ts @@ -0,0 +1,12 @@ +// 导入echarts核心模块 +import * as echarts from 'echarts/core' +// 导入主题JSON +import themeJson from './apipark-chart-palette.json' + +// 全局注册主题 +export function registerApiparkTheme() { + echarts.registerTheme('apipark', themeJson) +} + +// 导出主题名称,方便组件使用 +export const THEME_NAME = 'apipark' diff --git a/frontend/packages/common/src/const/charts/theme.ts b/frontend/packages/common/src/const/charts/theme.ts new file mode 100644 index 00000000..e340b378 --- /dev/null +++ b/frontend/packages/common/src/const/charts/theme.ts @@ -0,0 +1,11 @@ +// 导入主题配置 +import themeJson from './apipark-chart-palette.json' + +// 导出主题配置 +export const apiparkTheme = themeJson + +// 导出颜色列表,方便单独使用 +export const chartColors = themeJson.color + +// 导出默认颜色 +export const defaultColor = chartColors[0] diff --git a/frontend/packages/core/src/App.tsx b/frontend/packages/core/src/App.tsx index cb98a151..fc4d9cb1 100644 --- a/frontend/packages/core/src/App.tsx +++ b/frontend/packages/core/src/App.tsx @@ -6,6 +6,7 @@ import { PluginEventHubProvider } from '@common/contexts/PluginEventHubContext' import { PluginSlotHubProvider } from '@common/contexts/PluginSlotHubContext' import useInitializeMonaco from '@common/hooks/useInitializeMonaco' import { $t } from '@common/locales' +import { registerApiparkTheme } from '@common/const/charts/initChartTheme' import RenderRoutes from '@core/components/aoplatform/RenderRoutes' import { App as AppAntd, ConfigProvider } from 'antd' import { useMemo } from 'react' @@ -130,6 +131,9 @@ const antdComponentThemeToken = { } } +// 注册 ECharts 主题 +registerApiparkTheme() + function App() { const { locale } = useLocaleContext() useInitializeMonaco() diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index b3c33a44..478f18a6 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -22,13 +22,13 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha const chartRef = useRef(null) const [option, setOption] = useState({}) const setChartOption = (dataInfo: AreaChartInfo) => { + const hasData = dataInfo.data && dataInfo.data.length > 0 const option = { - tooltip: { + tooltip: hasData ? { trigger: 'axis', formatter: function (value: any) { // 如果是数组,取第一个参数的name const param = Array.isArray(value) ? value[0] : value - console.log('params==', param) let tooltipContent = `
` const marker = `` tooltipContent += `
@@ -37,6 +37,8 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha tooltipContent += '
' return tooltipContent } + } : { + show: false // 没有数据时不显示tooltip }, title: [ { @@ -88,6 +90,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha yAxis: { type: 'value', boundaryGap: [0, '5%'], + show: hasData, // 没有数据时不显示Y轴 axisLine: { show: false }, @@ -129,7 +132,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha // } // ], // 添加空状态提示 - graphic: !dataInfo.data.length + graphic: !hasData ? [ { type: 'text', @@ -225,7 +228,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
- +
) } diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index b42e2714..1759998e 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -1,7 +1,7 @@ import ECharts, { EChartsOption } from 'echarts-for-react' import { useEffect, useRef, useState } from 'react' import { $t } from '@common/locales/index.ts' -import { useGlobalContext } from '@common/contexts/GlobalStateContext' +import { chartColors, defaultColor } from '@common/const/charts/theme' export type BarChartInfo = { title: string @@ -24,7 +24,8 @@ type ServiceBarCharProps = { const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) - const [detaultColor] = useState('#5470c6') + // 使用从主题配置中导入的默认颜色,而不是硬编码的颜色值 + const [detaultColor] = useState(defaultColor) const tokenMap = { inputToken: $t('输入 Token'), outputToken: $t('输出 Token'), @@ -48,7 +49,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr : dataInfo.data // 为每个数据系列添加一行 data.forEach((item, index) => { - const color = item.color + // 使用与柱状图相同的颜色策略,确保颜色一致性 + const color = index < chartColors.length ? chartColors[index] : item.color const name = tokenMap[item.name as keyof typeof tokenMap] || item.name const value = item.value[dataInfo.date.indexOf(params.name)] || 0 @@ -92,7 +94,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr top: '110px', containLabel: true }, - tooltip: { + tooltip: hasData ? { trigger: 'axis', axisPointer: { type: 'shadow' @@ -102,6 +104,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const param = Array.isArray(params) ? params[0] : params return tooltipFormatter(param) } + } : { + show: false // 没有数据时不显示tooltip }, legend: { show: false, @@ -133,7 +137,9 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr name: '', min: 0, minInterval: 1, + show: hasData, // 没有数据时不显示Y轴 splitLine: { + show: hasData, // 没有数据时不显示网格线 lineStyle: { type: 'dashed', color: '#eee' @@ -202,7 +208,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr data: dataInfo.data } ] - : dataInfo.data.map((item) => ({ + : dataInfo.data.map((item, index) => ({ name: item.name, type: 'bar', stack: '总量', @@ -210,7 +216,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr focus: 'series' }, itemStyle: { - color: item.color + // 使用主题中的颜色列表,如果索引超出范围则使用项目自带的颜色 + color: index < chartColors.length ? chartColors[index] : item.color }, data: item.value })) @@ -252,7 +259,13 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr }, []) return (
- +
) } diff --git a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx index f0f8776e..386194d6 100644 --- a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx @@ -59,11 +59,11 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL }) } return ( -
+
{Object.keys(topRankingList)?.map((item: any, index: number) => ( 0 ? 'ml-[10px] md:ml-[10px] sm:ml-0 sm:mt-[10px]' : ''}`} + className={`flex-1 min-w-[430px] h-fit rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`} classNames={{ body: 'p-[15px]' }} @@ -75,7 +75,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL id={item} columns={[...columns]} minVirtualHeight={430} - noScroll={true} + noScroll request={() => getTableData(item)} showPagination={false} tableClass="ranking-list" diff --git a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx index b7773630..208215e1 100644 --- a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx @@ -319,7 +319,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ {barChartInfo?.map((item: BarChartInfo, index: number) => ( 0 ? 'ml-[10px]' : ''}`} + className={`flex-1 min-w-[430px] rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`} classNames={{ body: 'py-[15px] px-[0px]' }} @@ -332,7 +332,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ {perBarChartInfo?.map((item: any, index: number) => ( 0 ? 'ml-[10px]' : ''}`} + className={`flex-1 rounded-[10px] min-w-[284px] ${index > 0 ? 'ml-[10px]' : ''}`} classNames={{ body: 'py-[15px] px-[0px]' }} diff --git a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx index c22d4ec0..eb9b7963 100644 --- a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx +++ b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx @@ -326,7 +326,7 @@ export default function DashboardTotal() { {barChartInfo?.map((item: BarChartInfo, index: number) => ( 0 ? 'ml-[10px]' : ''}`} + className={`flex-1 min-w-[430px] rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`} classNames={{ body: 'py-[15px] px-[0px]' }} @@ -339,7 +339,7 @@ export default function DashboardTotal() { {perBarChartInfo?.map((item: any, index: number) => ( 0 ? 'ml-[10px]' : ''}`} + className={`flex-1 min-w-[284px] rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`} classNames={{ body: 'py-[15px] px-[0px]' }} From 5e05b2117eb5315a475b61574768bc2c084986d6 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 29 Apr 2025 19:28:35 +0800 Subject: [PATCH 14/29] feature/1.8-Improve system observability --- .../packages/core/src/pages/serviceLogs/LogDetail.tsx | 8 +++++--- .../src/pages/serviceOverview/rankingList/RankingList.tsx | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx index a3b222ad..b3991503 100644 --- a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -103,7 +103,7 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro */ const renderStatusWithColor = (status: string) => { // 获取状态码首位数字 - const firstDigit = status.charAt(0) + const firstDigit = String(status).charAt(0) let color = '' switch (firstDigit) { case '2': @@ -308,10 +308,12 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro isSystemConsumer: result.isSystemConsumer }) setRequestInfoData({ - Header: result.request.header + Header: result.request.header, + Body: result.response.body }) setResponseInfoData({ - Header: result.response.header + Header: result.response.header, + Body: result.response.body }) } else { message.error(msg || $t(RESPONSE_TIPS.error)) diff --git a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx index 386194d6..0b1f8bed 100644 --- a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx @@ -69,7 +69,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL }} >
- {item === 'TOP API' ? $t('API 使用排名') : $t('消费者使用排名')} + {item === 'TOP API' ? $t('API 使用排名') : $t('消费者使用排名')}
Date: Tue, 29 Apr 2025 19:33:24 +0800 Subject: [PATCH 15/29] feature/1.8-Improve system observability --- .../core/src/pages/system/api/SystemInsideRouterCreate.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx b/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx index ecf601d2..16cf380c 100644 --- a/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/system/api/SystemInsideRouterCreate.tsx @@ -115,7 +115,7 @@ const SystemInsideRouterCreate = forwardRef { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { - const { disable, protocols, path, methods, description, match, proxy } = data.router + const { disable, protocols, path, name, methods, description, match, proxy } = data.router let newPath = path let pathMatch = 'full' if (prefixForce && path?.startsWith(apiPrefix + '/')) { @@ -128,6 +128,7 @@ const SystemInsideRouterCreate = forwardRef Date: Tue, 29 Apr 2025 19:40:12 +0800 Subject: [PATCH 16/29] feature/1.8-Improve system observability --- frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx index b3991503..d5de8630 100644 --- a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -309,7 +309,7 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro }) setRequestInfoData({ Header: result.request.header, - Body: result.response.body + Body: result.request.body }) setResponseInfoData({ Header: result.response.header, From 32bc7354ba43d6a9e2c44080a04f541452462667 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 09:08:52 +0800 Subject: [PATCH 17/29] feature/1.8-Improve system observability --- frontend/packages/core/src/index.css | 7 ++++ .../serviceLogs/ApiNetWorkDataPreview.tsx | 2 +- .../core/src/pages/serviceLogs/LogDetail.tsx | 2 +- .../src/pages/serviceLogs/ServiceLogs.tsx | 7 ++-- .../charts/ServiceAreaChart.tsx | 18 +++++++---- .../serviceOverview/charts/ServiceBarChar.tsx | 32 +++++++++++-------- .../serviceOverview/indicator/Indicator.tsx | 4 +-- .../rankingList/RankingList.tsx | 2 +- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/frontend/packages/core/src/index.css b/frontend/packages/core/src/index.css index c7e7a958..8a5679e3 100644 --- a/frontend/packages/core/src/index.css +++ b/frontend/packages/core/src/index.css @@ -1175,6 +1175,13 @@ p{ .ant-alert-info{ background: #1784FC1A !important; } +.service-log-tab .ant-tabs .ant-tabs-nav .ant-tabs-tab{ + padding-left: 0px; + padding-right: 0px; +} +.service-log-tab .ant-tabs .ant-tabs-tab+.ant-tabs-tab { + margin-left: 15px; +} .monaco-editor .find-widget .monaco-inputbox.synthetic-focus{ outline-color: var(--primary-color) !important; diff --git a/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx index ce758d38..8b41e1ca 100644 --- a/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx @@ -37,7 +37,7 @@ const ApiNetWorkDataPreview = ({ configContent = {} }: { configContent?: { [key: <> {Object.keys(configContent).map((item) => { return ( -
+
{item}
{!configContent[item] ? ( diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx index d5de8630..b991bb67 100644 --- a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -349,7 +349,7 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro }} contentStyle={{ fontWeight: '600' }} /> -
+
@@ -228,14 +228,13 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' } spinning={dashboardLoading} > -
+
-
+
{ const chartRef = useRef(null) const [option, setOption] = useState({}) + const [hasData, setHasData] = useState(true) const setChartOption = (dataInfo: AreaChartInfo) => { - const hasData = dataInfo.data && dataInfo.data.length > 0 + const dataExists = dataInfo.data && dataInfo.data.length > 0 + // 更新hasData状态 + setHasData(dataExists) const option = { - tooltip: hasData ? { + tooltip: dataExists ? { trigger: 'axis', formatter: function (value: any) { // 如果是数组,取第一个参数的name @@ -49,7 +52,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha rich: { titleStyle: { fontSize: 14, - color: '#999', + color: '#999999', fontWeight: 'normal', lineHeight: 20 }, @@ -90,7 +93,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha yAxis: { type: 'value', boundaryGap: [0, '5%'], - show: hasData, // 没有数据时不显示Y轴 + show: dataExists, // 没有数据时不显示Y轴 axisLine: { show: false }, @@ -132,7 +135,8 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha // } // ], // 添加空状态提示 - graphic: !hasData + silent: !dataExists, + graphic: !dataExists ? [ { type: 'text', @@ -228,7 +232,9 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
- +
+ +
) } diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index 1759998e..c3b6f35e 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -26,6 +26,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const [option, setOption] = useState({}) // 使用从主题配置中导入的默认颜色,而不是硬编码的颜色值 const [detaultColor] = useState(defaultColor) + const [hasData, setHasData] = useState(true) const tokenMap = { inputToken: $t('输入 Token'), outputToken: $t('输出 Token'), @@ -34,7 +35,9 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const setChartOption = (dataInfo: BarChartInfo) => { const isNumberArray = typeof dataInfo.data[0] !== 'object' const legendData = isNumberArray ? [dataInfo.title] : dataInfo.data.map((item) => item.name) - const hasData = dataInfo.data && dataInfo.data.length > 0 + const dataExists = dataInfo.data && dataInfo.data.length > 0 + // 更新hasData状态 + setHasData(dataExists) const tooltipFormatter = (params: { name: string; color: string; seriesIndex?: number }) => { let tooltipContent = `
${isNumberArray ? '' : params.name}
` @@ -73,7 +76,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr rich: { titleStyle: { fontSize: 14, - color: '#999', + color: '#999999', fontWeight: 'normal', lineHeight: 20 }, @@ -94,7 +97,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr top: '110px', containLabel: true }, - tooltip: hasData ? { + tooltip: dataExists ? { trigger: 'axis', axisPointer: { type: 'shadow' @@ -137,9 +140,9 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr name: '', min: 0, minInterval: 1, - show: hasData, // 没有数据时不显示Y轴 + show: dataExists, // 没有数据时不显示Y轴 splitLine: { - show: hasData, // 没有数据时不显示网格线 + show: dataExists, // 没有数据时不显示网格线 lineStyle: { type: 'dashed', color: '#eee' @@ -179,7 +182,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr // } // ], // 添加空状态提示 - graphic: !hasData + silent: !dataExists, + graphic: !dataExists ? [ { type: 'text', @@ -259,13 +263,15 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr }, []) return (
- +
+ +
) } diff --git a/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx index d51f4de9..c4c48a6c 100644 --- a/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/indicator/Indicator.tsx @@ -72,11 +72,11 @@ const Indicator = ({ indicatorInfo }: { indicatorInfo: any }) => { } }} > -
+
{item.title} {item.link && }
-
+
{item.content}
diff --git a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx index 0b1f8bed..d5cf296f 100644 --- a/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/rankingList/RankingList.tsx @@ -69,7 +69,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL }} >
- {item === 'TOP API' ? $t('API 使用排名') : $t('消费者使用排名')} + {item === 'TOP API' ? $t('API 使用排名') : $t('消费者使用排名')}
Date: Wed, 30 Apr 2025 09:20:04 +0800 Subject: [PATCH 18/29] feature/1.8-Improve system observability --- .../core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx index 8b41e1ca..53d447da 100644 --- a/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/ApiNetWorkDataPreview.tsx @@ -35,7 +35,7 @@ const ApiNetWorkDataPreview = ({ configContent = {} }: { configContent?: { [key: return ( <> - {Object.keys(configContent).map((item) => { + {Object.keys(configContent).filter((item) => !!configContent[item]).map((item) => { return (
{item}
From 119e03fda8a17b094a5356e4be347eac1d1f03c3 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 09:29:46 +0800 Subject: [PATCH 19/29] feature/1.8-Improve system observability --- .../src/components/aoplatform/UnUsedWordForTranslate.tsx | 1 + frontend/packages/common/src/locales/scan/en-US.json | 3 ++- frontend/packages/common/src/locales/scan/ja-JP.json | 3 ++- frontend/packages/common/src/locales/scan/zh-CN.json | 3 ++- frontend/packages/common/src/locales/scan/zh-TW.json | 3 ++- frontend/packages/core/src/const/system/const.tsx | 4 ++-- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx b/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx index c38d7e0e..d28ad710 100644 --- a/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx +++ b/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx @@ -188,6 +188,7 @@ export const TranslateWord = () => { {$t('消费者 IP')} {$t('鉴权名称')} {$t('日志输出')} + {$t('响应时间')} ) } diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 386c0919..d909388b 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -964,5 +964,6 @@ "K3c722abd": "Log Output", "K74a5fbc0": "Log Output Settings", "Kc8bf447": "Request Log", - "Kf8525cf2": "No Data" + "Kf8525cf2": "No Data", + "K1be06929": "Response Time" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index 379bdb04..cbf6d9a5 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -986,5 +986,6 @@ "K3c722abd": "ログ出力", "K74a5fbc0": "ログ出力設定", "Kc8bf447": "リクエストログ", - "Kf8525cf2": "データがありません" + "Kf8525cf2": "データがありません", + "K1be06929": "応答時間" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index 85e21b30..fc41b93e 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -915,5 +915,6 @@ "K3c722abd": "日志输出", "K74a5fbc0": "日志输出设置", "Kc8bf447": "请求日志", - "Kf8525cf2": "暂无数据" + "Kf8525cf2": "暂无数据", + "K1be06929": "响应时间" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index 1d4cd1df..317109a3 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -986,5 +986,6 @@ "K3c722abd": "日誌輸出", "K74a5fbc0": "日誌輸出設定", "Kc8bf447": "請求日誌", - "Kf8525cf2": "暫無資料" + "Kf8525cf2": "暫無資料", + "K1be06929": "回應時間" } diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index 45935f93..a66fbd16 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -554,7 +554,7 @@ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ ellipsis: true }, { - title: 'API / Tools', + title: 'API / Tool', dataIndex: ['api', 'name'], ellipsis: true }, @@ -593,7 +593,7 @@ export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ ellipsis: true }, { - title: 'API / Tools', + title: 'API / Tool', dataIndex: ['api', 'name'], ellipsis: true }, From f1e5b29908f23b9b237b81542ff9108d28488c45 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 09:46:01 +0800 Subject: [PATCH 20/29] feature/1.8-Improve system observability --- .../aoplatform/UnUsedWordForTranslate.tsx | 1 + .../packages/common/src/locales/keyHashMap.json | 2 ++ .../packages/common/src/locales/scan/en-US.json | 3 ++- .../packages/common/src/locales/scan/ja-JP.json | 3 ++- .../packages/common/src/locales/scan/zh-CN.json | 3 ++- .../packages/common/src/locales/scan/zh-TW.json | 3 ++- frontend/packages/core/src/const/system/const.tsx | 13 +++++++++++-- .../core/src/pages/serviceLogs/LogDetail.tsx | 4 ++-- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx b/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx index d28ad710..7d9c228b 100644 --- a/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx +++ b/frontend/packages/common/src/components/aoplatform/UnUsedWordForTranslate.tsx @@ -189,6 +189,7 @@ export const TranslateWord = () => { {$t('鉴权名称')} {$t('日志输出')} {$t('响应时间')} + {$t('时间戳')} ) } diff --git a/frontend/packages/common/src/locales/keyHashMap.json b/frontend/packages/common/src/locales/keyHashMap.json index 59bf3985..ea37166c 100644 --- a/frontend/packages/common/src/locales/keyHashMap.json +++ b/frontend/packages/common/src/locales/keyHashMap.json @@ -247,6 +247,8 @@ "消费者 IP": "K1bc5e0a3", "鉴权名称": "K6f39ea21", "日志输出": "K3c722abd", + "响应时间": "K1be06929", + "时间戳": "K5e51f5d", "暂无操作权限,请联系管理员分配。": "K23fda291", "微信小程序": "K4618cb0a", "获取文件,需填路径": "Ka854f511", diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index d909388b..113ba88f 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -965,5 +965,6 @@ "K74a5fbc0": "Log Output Settings", "Kc8bf447": "Request Log", "Kf8525cf2": "No Data", - "K1be06929": "Response Time" + "K1be06929": "Response Time", + "K5e51f5d": "Timestamp" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index cbf6d9a5..1ccbeeca 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -987,5 +987,6 @@ "K74a5fbc0": "ログ出力設定", "Kc8bf447": "リクエストログ", "Kf8525cf2": "データがありません", - "K1be06929": "応答時間" + "K1be06929": "応答時間", + "K5e51f5d": "タイムスタンプ" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index fc41b93e..f5304de2 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -916,5 +916,6 @@ "K74a5fbc0": "日志输出设置", "Kc8bf447": "请求日志", "Kf8525cf2": "暂无数据", - "K1be06929": "响应时间" + "K1be06929": "响应时间", + "K5e51f5d": "时间" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index 317109a3..f0382c56 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -987,5 +987,6 @@ "K74a5fbc0": "日誌輸出設定", "Kc8bf447": "請求日誌", "Kf8525cf2": "暫無資料", - "K1be06929": "回應時間" + "K1be06929": "回應時間", + "K5e51f5d": "時間" } diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index a66fbd16..79d2b78f 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -549,8 +549,10 @@ export const REST_SERVICE_TOP_RANKING_LIST: PageProColumns[] = [ /** REST 服务日志 */ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ { - title: '时间', + title: '时间戳', dataIndex: 'logTime', + copyable: false, + width: 180, ellipsis: true }, { @@ -571,11 +573,14 @@ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ { title: 'IP', dataIndex: 'ip', + copyable: true, + width: 140, ellipsis: true }, { title: '响应时间', dataIndex: 'responseTime', + width: 130, ellipsis: true }, { @@ -588,8 +593,10 @@ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ /** AI 服务日志 */ export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ { - title: '时间', + title: '时间戳', dataIndex: 'logTime', + copyable: false, + width: 200, ellipsis: true }, { @@ -615,6 +622,8 @@ export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ { title: 'IP', dataIndex: 'ip', + copyable: true, + width: 140, ellipsis: true }, { diff --git a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx index b991bb67..5b4bb9f4 100644 --- a/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/LogDetail.tsx @@ -161,7 +161,7 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro setDescriptionItems([ { key: 'time', - label: $t('时间'), + label: $t('时间戳'), children: time }, { @@ -213,7 +213,7 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro setDescriptionItems([ { key: 'time', - label: $t('时间'), + label: $t('时间戳'), children: time }, { From 12a8317dd89c3a28ef56ab75540f5a8ef4b20408 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 10:25:06 +0800 Subject: [PATCH 21/29] feature/1.8-Improve system observability --- frontend/packages/core/src/const/system/const.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index 79d2b78f..b98f3ca9 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -556,7 +556,7 @@ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ ellipsis: true }, { - title: 'API / Tool', + title: 'API / Tools', dataIndex: ['api', 'name'], ellipsis: true }, @@ -600,7 +600,7 @@ export const AI_SERVICE_LOG_LIST: PageProColumns[] = [ ellipsis: true }, { - title: 'API / Tool', + title: 'API / Tools', dataIndex: ['api', 'name'], ellipsis: true }, From 18a3516e040bd7df9624f4e32b28bddf042cfc38 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 15:48:52 +0800 Subject: [PATCH 22/29] feature/1.8-Improve system observability --- .../packages/core/src/const/system/const.tsx | 2 +- .../src/pages/serviceLogs/ServiceLogs.tsx | 2 +- .../charts/ServiceAreaChart.tsx | 22 +++- .../serviceOverview/charts/ServiceBarChar.tsx | 122 +++++++++++++----- .../pages/serviceOverview/serviceOverview.tsx | 95 +++++++++----- .../core/src/pages/serviceOverview/utils.ts | 20 +-- .../dashboard/src/pages/DashboardTotal.tsx | 107 ++++++++++----- 7 files changed, 259 insertions(+), 111 deletions(-) diff --git a/frontend/packages/core/src/const/system/const.tsx b/frontend/packages/core/src/const/system/const.tsx index b98f3ca9..23bad822 100644 --- a/frontend/packages/core/src/const/system/const.tsx +++ b/frontend/packages/core/src/const/system/const.tsx @@ -562,7 +562,7 @@ export const REST_SERVICE_LOG_LIST: PageProColumns[] = [ }, { title: '消费者', - dataIndex: ['consumers', 'name'], + dataIndex: ['consumer', 'name'], ellipsis: true }, { diff --git a/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx index ce7eb1b3..71f13d8f 100644 --- a/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx +++ b/frontend/packages/core/src/pages/serviceLogs/ServiceLogs.tsx @@ -45,7 +45,7 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService' /** 当前选中的时间范围 */ const [timeRange, setTimeRange] = useState() /** 默认时间 */ - const [defaultTime] = useState('sevenDays') + const [defaultTime] = useState('day') /** 全局状态 */ const { state } = useGlobalContext() /** diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index 199f4d54..9e6d1a4a 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -16,9 +16,10 @@ type ServiceAreaCharProps = { customClassNames?: string dataInfo?: AreaChartInfo height?: number + showAvgLine?: boolean } -const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCharProps) => { +const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: ServiceAreaCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) const [hasData, setHasData] = useState(true) @@ -159,6 +160,25 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha itemStyle: { color: 'rgb(255, 70, 131)' }, + markLine: showAvgLine ? { + silent: false, + symbol: 'none', + lineStyle: { + width: 1, + type: 'dashed' + }, + label: { + position: 'insideEndTop', + formatter: '{c}', + color: '#000', + fontSize: 10, + backgroundColor: 'transparent', + padding: [10, 4], + borderRadius: 2, + distance: -5 + }, + data: [{ type: 'average', name: 'Avg' }] + } : undefined, areaStyle: { color: { type: 'linear', diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index c3b6f35e..685303a1 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -13,15 +13,25 @@ export type BarChartInfo = { value: number[] }[] showXAxis?: boolean + inputTokenTotal?: string + outputTokenTotal?: string + request2xxTotal?: string + request4xxTotal?: string + request5xxTotal?: string + traffic2xxTotal?: string + traffic4xxTotal?: string + traffic5xxTotal?: string } type ServiceBarCharProps = { customClassNames?: string dataInfo?: BarChartInfo height?: number + showAvgLine?: boolean + showLegendIndicator?: boolean } -const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => { +const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showLegendIndicator }: ServiceBarCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) // 使用从主题配置中导入的默认颜色,而不是硬编码的颜色值 @@ -29,8 +39,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr const [hasData, setHasData] = useState(true) const tokenMap = { inputToken: $t('输入 Token'), - outputToken: $t('输出 Token'), - totalToken: $t('总 Token') + outputToken: $t('输出 Token') } const setChartOption = (dataInfo: BarChartInfo) => { const isNumberArray = typeof dataInfo.data[0] !== 'object' @@ -53,7 +62,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr // 为每个数据系列添加一行 data.forEach((item, index) => { // 使用与柱状图相同的颜色策略,确保颜色一致性 - const color = index < chartColors.length ? chartColors[index] : item.color + const color = item.color ? item.color : index < chartColors.length ? chartColors[index] : detaultColor const name = tokenMap[item.name as keyof typeof tokenMap] || item.name const value = item.value[dataInfo.date.indexOf(params.name)] || 0 @@ -97,30 +106,43 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr top: '110px', containLabel: true }, - tooltip: dataExists ? { - trigger: 'axis', - axisPointer: { - type: 'shadow' - }, - formatter: function (params: any) { - // 如果是数组,取第一个参数的name - const param = Array.isArray(params) ? params[0] : params - return tooltipFormatter(param) - } - } : { - show: false // 没有数据时不显示tooltip - }, + tooltip: dataExists + ? { + trigger: 'axis', + axisPointer: { + type: 'shadow' + }, + formatter: function (params: any) { + // 如果是数组,取第一个参数的name + const param = Array.isArray(params) ? params[0] : params + return tooltipFormatter(param) + } + } + : { + show: false // 没有数据时不显示tooltip + }, legend: { - show: false, + show: !isNumberArray, data: legendData, right: '10px', - top: '30px', + top: '60px', itemWidth: 10, itemHeight: 10, textStyle: { color: '#333' }, - icon: 'rect' + icon: 'rect', + formatter: function(name: string): string { + // 这里可以映射或自定义图例文本 + const customNames: Record = { + 'inputToken': `${$t('输入 Token')} ${showLegendIndicator ? `(${dataInfo.inputTokenTotal})` : ''}`, + 'outputToken': `${$t('输出 Token')} ${showLegendIndicator ? `(${dataInfo.outputTokenTotal})` : ''}`, + '2xx': `${'2xx'} ${showLegendIndicator ? `(${dataInfo.request2xxTotal || dataInfo.traffic2xxTotal})` : ''}`, + '4xx': `${'4xx'} ${showLegendIndicator ? `(${dataInfo.request4xxTotal || dataInfo.traffic4xxTotal})` : ''}`, + '5xx': `${'5xx'} ${showLegendIndicator ? `(${dataInfo.request5xxTotal || dataInfo.traffic5xxTotal})` : ''}` + }; + return customNames[name] || name; + } }, xAxis: { type: 'category', @@ -209,6 +231,27 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr itemStyle: { color: detaultColor }, + markLine: showAvgLine + ? { + silent: false, + symbol: 'none', + lineStyle: { + width: 1, + type: 'dashed' + }, + label: { + position: 'insideEndTop', + formatter: '{c}', + color: '#000', + fontSize: 10, + backgroundColor: 'transparent', + padding: [10, 4], + borderRadius: 2, + distance: -5 + }, + data: [{ type: 'average', name: 'Avg' }] + } + : undefined, data: dataInfo.data } ] @@ -216,12 +259,33 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr name: item.name, type: 'bar', stack: '总量', + markLine: showAvgLine + ? { + silent: false, + symbol: 'none', + lineStyle: { + width: 1, + type: 'dashed' + }, + label: { + position: 'insideEndTop', + formatter: '{c}', + color: '#000', + fontSize: 10, + backgroundColor: 'transparent', + padding: [10, 4], + borderRadius: 2, + distance: -5 + }, + data: [{ type: 'average', name: 'Avg' }] + } + : undefined, emphasis: { focus: 'series' }, itemStyle: { // 使用主题中的颜色列表,如果索引超出范围则使用项目自带的颜色 - color: index < chartColors.length ? chartColors[index] : item.color + color: item.color ? item.color : index < chartColors.length ? chartColors[index] : detaultColor }, data: item.value })) @@ -232,7 +296,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr // 使用深度监听来确保图表数据更新 useEffect(() => { if (!dataInfo) return - + // 直接获取 ECharts 实例并设置选项 const echartsInstance = chartRef.current?.getEchartsInstance() if (echartsInstance) { @@ -252,10 +316,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr echartsInstance.resize() } } - + // 添加监听 window.addEventListener('resize', handleResize) - + // 组件卸载时移除监听 return () => { window.removeEventListener('resize', handleResize) @@ -264,11 +328,11 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr return (
-
diff --git a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx index 208215e1..818b973c 100644 --- a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx @@ -22,7 +22,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ /** 面板 loading */ const [dashboardLoading, setDashboardLoading] = useState(true) /** 默认时间 */ - const [defaultTime] = useState('sevenDays') + const [defaultTime] = useState('day') /** 当前选中的时间范围 */ const [timeRange, setTimeRange] = useState() /** 总数数据 */ @@ -73,7 +73,12 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ 'avg_token_per_subscriber', 'input_token', 'output_token', - 'total_token' + 'total_token', + 'request_2xx_total', + 'request_4xx_total', + 'request_5xx_total', + 'input_token_total', + 'output_token_total' ] }).then((response) => { const { code, data, msg } = response @@ -105,19 +110,29 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // 设置总数数据 setBarChartInfo([ // 服务请求次数 - setBarChartInfoData({ - title: $t('请求数'), - data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + request2xxTotal: serviceOverview.request2xxTotal, + request4xxTotal: serviceOverview.request4xxTotal, + request5xxTotal: serviceOverview.request5xxTotal + }, // 流量消耗总数 - setBarChartInfoData({ - title: $t('流量'), - data: serviceOverview.trafficOverview, - value: serviceOverview.trafficTotal, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('流量'), + data: serviceOverview.trafficOverview, + value: serviceOverview.trafficTotal, + date: serviceOverview.date + }), + traffic2xxTotal: serviceOverview.traffic2xxTotal, + traffic4xxTotal: serviceOverview.traffic4xxTotal, + traffic5xxTotal: serviceOverview.traffic5xxTotal + } ]) // 设置平均值数据 setPerBarChartInfo([ @@ -167,22 +182,31 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // 设置总数数据 setBarChartInfo([ // 服务请求次数 - setBarChartInfoData({ - title: $t('请求数'), - data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + request2xxTotal: serviceOverview.request2xxTotal, + request4xxTotal: serviceOverview.request4xxTotal, + request5xxTotal: serviceOverview.request5xxTotal + }, // token 消耗总数 - setBarChartInfoData({ - title: $t('Token'), - data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ - inputToken: item.inputToken, - outputToken: item.outputToken - })), - value: serviceOverview.tokenTotal, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('Token'), + data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ + inputToken: item.inputToken, + outputToken: item.outputToken + })), + value: serviceOverview.tokenTotal, + date: serviceOverview.date + }), + inputTokenTotal: serviceOverview.inputTokenTotal, + outputTokenTotal: serviceOverview.outputTokenTotal + } ]) // 设置平均值数据 setPerBarChartInfo([ @@ -238,7 +262,13 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ 'min_response_time', 'avg_response_time', 'avg_request_per_subscriber', - 'avg_traffic_per_subscriber' + 'avg_traffic_per_subscriber', + 'request_2xx_total', + 'request_4xx_total', + 'request_5xx_total', + 'traffic_2xx_total', + 'traffic_4xx_total', + 'traffic_5xx_total' ] }).then((response) => { const { code, data, msg } = response @@ -324,7 +354,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ body: 'py-[15px] px-[0px]' }} > - + ))}
@@ -343,11 +373,12 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ key={index} height={270} dataInfo={item} + showAvgLine={true} customClassNames="flex-1 relative" > ) : ( - + )} ))} diff --git a/frontend/packages/core/src/pages/serviceOverview/utils.ts b/frontend/packages/core/src/pages/serviceOverview/utils.ts index 94978192..6ec42e3b 100644 --- a/frontend/packages/core/src/pages/serviceOverview/utils.ts +++ b/frontend/packages/core/src/pages/serviceOverview/utils.ts @@ -29,25 +29,15 @@ export const setBarChartInfoData = ({ title, value, data, date, showXAxis }: Bar const keys = Object.keys(data[0]) // 定义颜色映射 const colorMap: Record = { - '2xx': '#7EC26A', - '4xx': '#F2CF59', - '5xx': '#F17975', - inputToken:'#7EC26A', - outputToken: '#F2CF59', - totalToken: '#F17975', - '200': '#7EC26A', - '400': '#F2CF59', - '500': '#F17975' + '2xx': '#3ba272', + '4xx': '#ffc404', + '5xx': '#b92325' } // 为每个键创建一个数据集 - const transformedData = keys.map((key, index) => { + const transformedData = keys.map((key) => { // 为没有映射颜色的键生成随机颜色 - const color = - colorMap[key] || - `#${Math.floor(Math.random() * 16777215) - .toString(16) - .padStart(6, '0')}` + const color = colorMap[key] return { name: key, diff --git a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx index eb9b7963..023a2ead 100644 --- a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx +++ b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx @@ -20,7 +20,7 @@ export default function DashboardTotal() { /** 获取数据 */ const { fetchData } = useFetch() /** 默认时间 */ - const [defaultTime] = useState('sevenDays') + const [defaultTime] = useState('day') /** 当前选中的时间范围 */ const [timeRange, setTimeRange] = useState() /** 当前激活的标签 */ @@ -66,7 +66,12 @@ export default function DashboardTotal() { 'avg_token_per_subscriber', 'input_token', 'output_token', - 'total_token' + 'total_token', + 'request_2xx_total', + 'request_4xx_total', + 'request_5xx_total', + 'input_token_total', + 'output_token_total' ] }).then((response) => { const { code, data, msg } = response @@ -98,7 +103,13 @@ export default function DashboardTotal() { 'max_response_time', 'min_response_time', 'avg_request_per_subscriber', - 'avg_traffic_per_subscriber' + 'avg_traffic_per_subscriber', + 'request_2xx_total', + 'request_4xx_total', + 'request_5xx_total', + 'traffic_2xx_total', + 'traffic_4xx_total', + 'traffic_5xx_total' ] }).then((response) => { const { code, data, msg } = response @@ -120,19 +131,29 @@ export default function DashboardTotal() { // 设置总数数据 setBarChartInfo([ // 服务请求次数 - setBarChartInfoData({ - title: $t('请求数'), - data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + request2xxTotal: serviceOverview.request2xxTotal, + request4xxTotal: serviceOverview.request4xxTotal, + request5xxTotal: serviceOverview.request5xxTotal + }, // 流量消耗总数 - setBarChartInfoData({ - title: $t('流量'), - data: serviceOverview.trafficOverview, - value: serviceOverview.trafficTotal, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('流量'), + data: serviceOverview.trafficOverview, + value: serviceOverview.trafficTotal, + date: serviceOverview.date + }), + traffic2xxTotal: serviceOverview.traffic2xxTotal, + traffic4xxTotal: serviceOverview.traffic4xxTotal, + traffic5xxTotal: serviceOverview.traffic5xxTotal + } ]) // 设置平均值数据 setPerBarChartInfo([ @@ -170,22 +191,31 @@ export default function DashboardTotal() { // 设置总数数据 setBarChartInfo([ // 服务请求次数 - setBarChartInfoData({ - title: $t('请求数'), - data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('请求数'), + data: serviceOverview.requestOverview, + value: serviceOverview.requestTotal, + date: serviceOverview.date + }), + request2xxTotal: serviceOverview.request2xxTotal, + request4xxTotal: serviceOverview.request4xxTotal, + request5xxTotal: serviceOverview.request5xxTotal + }, // token 消耗总数 - setBarChartInfoData({ - title: $t('Token'), - data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ - inputToken: item.inputToken, - outputToken: item.outputToken - })), - value: serviceOverview.tokenTotal, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('Token'), + data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ + inputToken: item.inputToken, + outputToken: item.outputToken + })), + value: serviceOverview.tokenTotal, + date: serviceOverview.date + }), + inputTokenTotal: serviceOverview.inputTokenTotal, + outputTokenTotal: serviceOverview.outputTokenTotal + } ]) // 设置平均值数据 setPerBarChartInfo([ @@ -331,7 +361,13 @@ export default function DashboardTotal() { body: 'py-[15px] px-[0px]' }} > - + ))}
@@ -349,12 +385,19 @@ export default function DashboardTotal() { ) : ( - + )} ))} From d50fbcdf4f6c8a24aec6272bb8fd70aa353df0c9 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 16:33:22 +0800 Subject: [PATCH 23/29] feature/1.8-Improve system observability --- .../common/src/locales/keyHashMap.json | 15 +++++++-------- .../common/src/locales/scan/en-US.json | 8 +++++++- .../common/src/locales/scan/ja-JP.json | 8 +++++++- .../common/src/locales/scan/zh-CN.json | 8 +++++++- .../common/src/locales/scan/zh-TW.json | 8 +++++++- .../charts/ServiceAreaChart.tsx | 9 +++++++++ .../serviceOverview/charts/ServiceBarChar.tsx | 18 ++++++++++++++++++ .../pages/serviceOverview/serviceOverview.tsx | 18 +++++++++--------- .../dashboard/src/pages/DashboardTotal.tsx | 18 +++++++++--------- 9 files changed, 80 insertions(+), 30 deletions(-) diff --git a/frontend/packages/common/src/locales/keyHashMap.json b/frontend/packages/common/src/locales/keyHashMap.json index ea37166c..f4d84dd4 100644 --- a/frontend/packages/common/src/locales/keyHashMap.json +++ b/frontend/packages/common/src/locales/keyHashMap.json @@ -673,20 +673,19 @@ "暂无数据": "Kf8525cf2", "输入 Token": "K33bc1ad1", "输出 Token": "Ke00ff18b", - "总 Token": "K81140e5b", "订阅数量": "Ke04bc00d", "已开启": "K1b97ae0a", "开启 MCP": "K19ec733b", "API 使用排名": "Kbee2340", "消费者使用排名": "Kf6af1f40", - "请求数": "K318a7519", - "流量": "K53eb7414", + "请求次数": "K9d3f2d9d", + "网络流量": "Ke2241377", "平均响应时间": "K7c8d5c23", - "平均请求数": "K652843b0", - "平均流量": "K8158a6e4", - "Token": "K9ef68e3f", - "平均 Token/s 统计": "K6c016898", - "平均 Token/订阅者统计": "Kf5eeb9c5", + "平均每消费者的请求次数": "K6c267c7b", + "平均每消费者的网络流量": "K133d4291", + "Token 消耗": "K37c5f1d0", + "平均 Token 消耗": "K10a8bee3", + "平均每消费者的 Token 消耗": "Kb98264d4", "单位:ms,最小值:1": "K2a16c93b", "API 路由设置": "Ka945cfb1", "API 基础信息": "K2e050340", diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 113ba88f..8487ce4a 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -966,5 +966,11 @@ "Kc8bf447": "Request Log", "Kf8525cf2": "No Data", "K1be06929": "Response Time", - "K5e51f5d": "Timestamp" + "K5e51f5d": "Timestamp", + "K9d3f2d9d": "Requests", + "Ke2241377": "Traffic", + "K6c267c7b": "Avg Requests per Subscriber", + "K133d4291": "Avg Traffic per Subscriber", + "K37c5f1d0": "Token", + "Kb98264d4": "Avg Token per Subscriber" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index 1ccbeeca..974ddd23 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -988,5 +988,11 @@ "Kc8bf447": "リクエストログ", "Kf8525cf2": "データがありません", "K1be06929": "応答時間", - "K5e51f5d": "タイムスタンプ" + "K5e51f5d": "タイムスタンプ", + "K9d3f2d9d": "リクエスト数", + "Ke2241377": "ネットワークトラフィック", + "K6c267c7b": "消費者あたりの平均リクエスト数", + "K133d4291": "消費者あたりの平均ネットワークトラフィック", + "K37c5f1d0": "トークン消費量", + "Kb98264d4": "消費者あたりの平均トークン消費量" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index f5304de2..087cb301 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -917,5 +917,11 @@ "Kc8bf447": "请求日志", "Kf8525cf2": "暂无数据", "K1be06929": "响应时间", - "K5e51f5d": "时间" + "K5e51f5d": "时间", + "K9d3f2d9d": "请求次数", + "Ke2241377": "网络流量", + "K6c267c7b": "平均每消费者的请求次数", + "K133d4291": "平均每消费者的网络流量", + "K37c5f1d0": "Token 消耗", + "Kb98264d4": "平均每消费者的 Token 消耗" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index f0382c56..59c1f7ba 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -988,5 +988,11 @@ "Kc8bf447": "請求日誌", "Kf8525cf2": "暫無資料", "K1be06929": "回應時間", - "K5e51f5d": "時間" + "K5e51f5d": "時間", + "K9d3f2d9d": "請求次數", + "Ke2241377": "網路流量", + "K6c267c7b": "平均每位使用者的請求次數", + "K133d4291": "平均每位使用者的網路流量", + "K37c5f1d0": "Token 消耗", + "Kb98264d4": "平均每位使用者的 Token 消耗" } diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index 9e6d1a4a..6c952d3e 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -168,6 +168,7 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: S type: 'dashed' }, label: { + show: false, position: 'insideEndTop', formatter: '{c}', color: '#000', @@ -177,6 +178,14 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: S borderRadius: 2, distance: -5 }, + emphasis: { + lineStyle: { + width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 + }, + label: { + show: false // 悬停时不显示标签 + } + }, data: [{ type: 'average', name: 'Avg' }] } : undefined, areaStyle: { diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index 685303a1..684b589f 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -240,6 +240,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL type: 'dashed' }, label: { + show: false, position: 'insideEndTop', formatter: '{c}', color: '#000', @@ -249,6 +250,14 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL borderRadius: 2, distance: -5 }, + emphasis: { + lineStyle: { + width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 + }, + label: { + show: false // 悬停时不显示标签 + } + }, data: [{ type: 'average', name: 'Avg' }] } : undefined, @@ -268,6 +277,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL type: 'dashed' }, label: { + show: false, position: 'insideEndTop', formatter: '{c}', color: '#000', @@ -277,6 +287,14 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL borderRadius: 2, distance: -5 }, + emphasis: { + lineStyle: { + width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 + }, + label: { + show: false // 悬停时不显示标签 + } + }, data: [{ type: 'average', name: 'Avg' }] } : undefined, diff --git a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx index 818b973c..2d2b711f 100644 --- a/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/serviceOverview.tsx @@ -112,7 +112,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // 服务请求次数 { ...setBarChartInfoData({ - title: $t('请求数'), + title: $t('请求次数'), data: serviceOverview.requestOverview, value: serviceOverview.requestTotal, date: serviceOverview.date @@ -124,7 +124,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // 流量消耗总数 { ...setBarChartInfoData({ - title: $t('流量'), + title: $t('网络流量'), data: serviceOverview.trafficOverview, value: serviceOverview.trafficTotal, date: serviceOverview.date @@ -149,7 +149,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ }, // 平均请求 setBarChartInfoData({ - title: $t('平均请求数'), + title: $t('平均每消费者的请求次数'), data: serviceOverview.avgRequestPerSubscriberOverview, value: serviceOverview.avgRequestPerSubscriber, date: serviceOverview.date, @@ -157,7 +157,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ }), // 平均流量消耗 setBarChartInfoData({ - title: $t('平均流量'), + title: $t('平均每消费者的网络流量'), data: serviceOverview.avgTrafficPerSubscriberOverview, value: serviceOverview.avgTrafficPerSubscriber, date: serviceOverview.date, @@ -184,7 +184,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // 服务请求次数 { ...setBarChartInfoData({ - title: $t('请求数'), + title: $t('请求次数'), data: serviceOverview.requestOverview, value: serviceOverview.requestTotal, date: serviceOverview.date @@ -196,7 +196,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ // token 消耗总数 { ...setBarChartInfoData({ - title: $t('Token'), + title: $t('Token 消耗'), data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ inputToken: item.inputToken, outputToken: item.outputToken @@ -212,7 +212,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ setPerBarChartInfo([ // 平均 token 消耗 { - title: $t('平均 Token/s 统计'), + title: $t('平均 Token 消耗'), data: serviceOverview.avgTokenOverview, value: serviceOverview.avgToken, date: serviceOverview.date, @@ -222,14 +222,14 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ }, // 平均请求 setBarChartInfoData({ - title: $t('平均请求数'), + title: $t('平均每消费者的请求次数'), data: serviceOverview.avgRequestPerSubscriberOverview, value: serviceOverview.avgRequestPerSubscriber, date: serviceOverview.date }), // 评价 token 消耗 setBarChartInfoData({ - title: $t('平均 Token/订阅者统计'), + title: $t('平均每消费者的 Token 消耗'), data: serviceOverview.avgTokenPerSubscriberOverview.map((item: { inputToken: number; outputToken: number }) => ({ inputToken: item.inputToken, outputToken: item.outputToken diff --git a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx index 023a2ead..316b0b57 100644 --- a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx +++ b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx @@ -133,7 +133,7 @@ export default function DashboardTotal() { // 服务请求次数 { ...setBarChartInfoData({ - title: $t('请求数'), + title: $t('请求次数'), data: serviceOverview.requestOverview, value: serviceOverview.requestTotal, date: serviceOverview.date @@ -145,7 +145,7 @@ export default function DashboardTotal() { // 流量消耗总数 { ...setBarChartInfoData({ - title: $t('流量'), + title: $t('网络流量'), data: serviceOverview.trafficOverview, value: serviceOverview.trafficTotal, date: serviceOverview.date @@ -169,14 +169,14 @@ export default function DashboardTotal() { }, // 平均请求 setBarChartInfoData({ - title: $t('平均请求数'), + title: $t('平均每消费者的请求次数'), data: serviceOverview.avgRequestPerSubscriberOverview, value: serviceOverview.avgRequestPerSubscriber, date: serviceOverview.date }), // 平均流量消耗 setBarChartInfoData({ - title: $t('平均流量'), + title: $t('平均每消费者的网络流量'), data: serviceOverview.avgTrafficPerSubscriberOverview, value: serviceOverview.avgTrafficPerSubscriber, date: serviceOverview.date @@ -193,7 +193,7 @@ export default function DashboardTotal() { // 服务请求次数 { ...setBarChartInfoData({ - title: $t('请求数'), + title: $t('请求次数'), data: serviceOverview.requestOverview, value: serviceOverview.requestTotal, date: serviceOverview.date @@ -205,7 +205,7 @@ export default function DashboardTotal() { // token 消耗总数 { ...setBarChartInfoData({ - title: $t('Token'), + title: $t('Token 消耗'), data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({ inputToken: item.inputToken, outputToken: item.outputToken @@ -221,7 +221,7 @@ export default function DashboardTotal() { setPerBarChartInfo([ // 平均 token 消耗 { - title: $t('平均 Token/s 统计'), + title: $t('平均 Token 消耗'), data: serviceOverview.avgTokenOverview, value: serviceOverview.avgToken, date: serviceOverview.date, @@ -231,14 +231,14 @@ export default function DashboardTotal() { }, // 平均请求 setBarChartInfoData({ - title: $t('平均请求数'), + title: $t('平均每消费者的请求次数'), data: serviceOverview.avgRequestPerSubscriberOverview, value: serviceOverview.avgRequestPerSubscriber, date: serviceOverview.date }), // 平均 token 消耗 setBarChartInfoData({ - title: $t('平均 Token/订阅者统计'), + title: $t('平均每消费者的 Token 消耗'), data: serviceOverview.avgTokenPerSubscriberOverview.map( (item: { inputToken: number; outputToken: number }) => ({ inputToken: item.inputToken, From c0dc5bcdb5346249fce4d12c286ed063af958661 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 17:17:00 +0800 Subject: [PATCH 24/29] feature/1.8-Improve system observability --- .../src/pages/serviceOverview/charts/ServiceAreaChart.tsx | 5 ++++- .../core/src/pages/serviceOverview/charts/ServiceBarChar.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index 6c952d3e..9edd3647 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -211,7 +211,10 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: S } ] } - setOption(option) + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + echartsInstance.setOption(option) + } } // 使用深度监听来确保图表数据更新 useEffect(() => { diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index 684b589f..49b2a61c 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -308,7 +308,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL data: item.value })) } - setOption(option) + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + echartsInstance.setOption(option) + } } // 使用深度监听来确保图表数据更新 From b91830b089c73ede749540feaae965c1c24b8f54 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Wed, 30 Apr 2025 19:09:14 +0800 Subject: [PATCH 25/29] feature/1.8-Improve system observability --- .../core/src/pages/serviceOverview/charts/ServiceBarChar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index 49b2a61c..cb594bd5 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -161,7 +161,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL type: 'value', name: '', min: 0, - minInterval: 1, + ...(showAvgLine ? {} : { minInterval: 1 }), show: dataExists, // 没有数据时不显示Y轴 splitLine: { show: dataExists, // 没有数据时不显示网格线 From 131a1fae591a7fa580958ef7a6d644d99ccd453b Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 6 May 2025 11:09:18 +0800 Subject: [PATCH 26/29] feature/1.8-Improve system observability --- .../charts/ServiceAreaChart.tsx | 8 +- .../serviceOverview/charts/ServiceBarChar.tsx | 55 +++++-- .../pages/serviceOverview/serviceOverview.tsx | 148 +++++++++++------- .../core/src/pages/serviceOverview/utils.ts | 2 +- .../dashboard/src/pages/DashboardTotal.tsx | 130 ++++++++------- .../packages/dashboard/src/utils/dashboard.ts | 111 +++++++++++++ 6 files changed, 329 insertions(+), 125 deletions(-) diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx index 9edd3647..015d2970 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceAreaChart.tsx @@ -9,6 +9,7 @@ type AreaChartInfo = { data: number[] max: string min: string + originValue?: number showXAxis?: boolean } @@ -17,9 +18,10 @@ type ServiceAreaCharProps = { dataInfo?: AreaChartInfo height?: number showAvgLine?: boolean + customMarkLineValue?: number } -const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: ServiceAreaCharProps) => { +const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine, customMarkLineValue }: ServiceAreaCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) const [hasData, setHasData] = useState(true) @@ -186,7 +188,9 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: S show: false // 悬停时不显示标签 } }, - data: [{ type: 'average', name: 'Avg' }] + data: dataInfo?.originValue !== undefined ? + [{ yAxis: dataInfo?.originValue, name: '自定义值' }] : + [{ type: 'average', name: 'Avg' }] } : undefined, areaStyle: { color: { diff --git a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx index cb594bd5..1267f2bd 100644 --- a/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx +++ b/frontend/packages/core/src/pages/serviceOverview/charts/ServiceBarChar.tsx @@ -21,6 +21,8 @@ export type BarChartInfo = { traffic2xxTotal?: string traffic4xxTotal?: string traffic5xxTotal?: string + max?: string | number + min?: string | number } type ServiceBarCharProps = { @@ -29,9 +31,17 @@ type ServiceBarCharProps = { height?: number showAvgLine?: boolean showLegendIndicator?: boolean + hideIndicatorValue?: boolean } -const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showLegendIndicator }: ServiceBarCharProps) => { +const ServiceBarChar = ({ + customClassNames, + dataInfo, + height, + showAvgLine, + showLegendIndicator, + hideIndicatorValue +}: ServiceBarCharProps) => { const chartRef = useRef(null) const [option, setOption] = useState({}) // 使用从主题配置中导入的默认颜色,而不是硬编码的颜色值 @@ -78,7 +88,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL const option: EChartsOption = { title: [ { - text: '{titleStyle|' + $t(dataInfo.title) + '}\n\n{valueStyle|' + dataInfo.value + '}', + text: + '{titleStyle|' + + $t(dataInfo.title) + + `}${hideIndicatorValue ? '' : '\n\n{valueStyle|' + dataInfo.value + '}'}`, left: '2%', top: '0', textStyle: { @@ -125,23 +138,23 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL show: !isNumberArray, data: legendData, right: '10px', - top: '60px', + top: hideIndicatorValue ? '10px' : '60px', itemWidth: 10, itemHeight: 10, textStyle: { color: '#333' }, icon: 'rect', - formatter: function(name: string): string { + formatter: function (name: string): string { // 这里可以映射或自定义图例文本 const customNames: Record = { - 'inputToken': `${$t('输入 Token')} ${showLegendIndicator ? `(${dataInfo.inputTokenTotal})` : ''}`, - 'outputToken': `${$t('输出 Token')} ${showLegendIndicator ? `(${dataInfo.outputTokenTotal})` : ''}`, + inputToken: `${$t('输入 Token')} ${showLegendIndicator ? `(${dataInfo.inputTokenTotal})` : ''}`, + outputToken: `${$t('输出 Token')} ${showLegendIndicator ? `(${dataInfo.outputTokenTotal})` : ''}`, '2xx': `${'2xx'} ${showLegendIndicator ? `(${dataInfo.request2xxTotal || dataInfo.traffic2xxTotal})` : ''}`, '4xx': `${'4xx'} ${showLegendIndicator ? `(${dataInfo.request4xxTotal || dataInfo.traffic4xxTotal})` : ''}`, '5xx': `${'5xx'} ${showLegendIndicator ? `(${dataInfo.request5xxTotal || dataInfo.traffic5xxTotal})` : ''}` - }; - return customNames[name] || name; + } + return customNames[name] || name } }, xAxis: { @@ -252,10 +265,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL }, emphasis: { lineStyle: { - width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 + width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 }, label: { - show: false // 悬停时不显示标签 + show: false // 悬停时不显示标签 } }, data: [{ type: 'average', name: 'Avg' }] @@ -289,10 +302,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL }, emphasis: { lineStyle: { - width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 + width: 1 // 保持线条宽度不变,禁用默认的悬停加粗 }, label: { - show: false // 悬停时不显示标签 + show: false // 悬停时不显示标签 } }, data: [{ type: 'average', name: 'Avg' }] @@ -348,6 +361,24 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showL }, []) return (
+ { + hideIndicatorValue && ( +
+
+
+
+ +
+ {dataInfo?.max} +
+ +
+ {dataInfo?.min} +
+
+
+ ) + }
{ const { code, data, msg } = response @@ -114,24 +118,24 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ ...setBarChartInfoData({ title: $t('请求次数'), data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, + value: formatNumberWithUnit(serviceOverview.requestTotal), date: serviceOverview.date }), - request2xxTotal: serviceOverview.request2xxTotal, - request4xxTotal: serviceOverview.request4xxTotal, - request5xxTotal: serviceOverview.request5xxTotal + request2xxTotal: formatNumberWithUnit(serviceOverview.request2xxTotal), + request4xxTotal: formatNumberWithUnit(serviceOverview.request4xxTotal), + request5xxTotal: formatNumberWithUnit(serviceOverview.request5xxTotal) }, // 流量消耗总数 { ...setBarChartInfoData({ title: $t('网络流量'), data: serviceOverview.trafficOverview, - value: serviceOverview.trafficTotal, + value: formatBytes(serviceOverview.trafficTotal), date: serviceOverview.date }), - traffic2xxTotal: serviceOverview.traffic2xxTotal, - traffic4xxTotal: serviceOverview.traffic4xxTotal, - traffic5xxTotal: serviceOverview.traffic5xxTotal + traffic2xxTotal: formatBytes(serviceOverview.traffic2xxTotal), + traffic4xxTotal: formatBytes(serviceOverview.traffic4xxTotal), + traffic5xxTotal: formatBytes(serviceOverview.traffic5xxTotal) } ]) // 设置平均值数据 @@ -140,29 +144,36 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ { title: $t('平均响应时间'), data: serviceOverview.avgResponseTimeOverview, - value: serviceOverview.avgResponseTime, + value: formatDuration(serviceOverview.avgResponseTime), + originValue: serviceOverview.avgResponseTime, date: serviceOverview.date, - max: serviceOverview.maxResponseTime, - min: serviceOverview.minResponseTime, + max: formatDuration(serviceOverview.maxResponseTime), + min: formatDuration(serviceOverview.minResponseTime), type: 'area', showXAxis: false }, // 平均请求 - setBarChartInfoData({ - title: $t('平均每消费者的请求次数'), - data: serviceOverview.avgRequestPerSubscriberOverview, - value: serviceOverview.avgRequestPerSubscriber, - date: serviceOverview.date, - showXAxis: false - }), + { + ...setBarChartInfoData({ + title: $t('平均每消费者的请求次数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + date: serviceOverview.date, + showXAxis: false + }), + max: abbreviateFloat(serviceOverview.maxRequestPerSubscriber), + min: abbreviateFloat(serviceOverview.minRequestPerSubscriber) + }, // 平均流量消耗 - setBarChartInfoData({ - title: $t('平均每消费者的网络流量'), - data: serviceOverview.avgTrafficPerSubscriberOverview, - value: serviceOverview.avgTrafficPerSubscriber, - date: serviceOverview.date, - showXAxis: false - }) + { + ...setBarChartInfoData({ + title: $t('平均每消费者的网络流量'), + data: serviceOverview.avgTrafficPerSubscriberOverview, + date: serviceOverview.date, + showXAxis: false + }), + max: formatBytes(serviceOverview.maxTrafficPerSubscriber), + min: formatBytes(serviceOverview.minTrafficPerSubscriber) + } ]) } @@ -186,12 +197,12 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ ...setBarChartInfoData({ title: $t('请求次数'), data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, + value: formatNumberWithUnit(serviceOverview.requestTotal), date: serviceOverview.date }), - request2xxTotal: serviceOverview.request2xxTotal, - request4xxTotal: serviceOverview.request4xxTotal, - request5xxTotal: serviceOverview.request5xxTotal + request2xxTotal: formatNumberWithUnit(serviceOverview.request2xxTotal), + request4xxTotal: formatNumberWithUnit(serviceOverview.request4xxTotal), + request5xxTotal: formatNumberWithUnit(serviceOverview.request5xxTotal) }, // token 消耗总数 { @@ -201,11 +212,11 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ inputToken: item.inputToken, outputToken: item.outputToken })), - value: serviceOverview.tokenTotal, + value: formatNumberWithUnit(serviceOverview.tokenTotal), date: serviceOverview.date }), - inputTokenTotal: serviceOverview.inputTokenTotal, - outputTokenTotal: serviceOverview.outputTokenTotal + inputTokenTotal: formatNumberWithUnit(serviceOverview.inputTokenTotal), + outputTokenTotal: formatNumberWithUnit(serviceOverview.outputTokenTotal) } ]) // 设置平均值数据 @@ -214,29 +225,38 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ { title: $t('平均 Token 消耗'), data: serviceOverview.avgTokenOverview, - value: serviceOverview.avgToken, + value: formatNumberWithUnit(serviceOverview.avgToken) + ' Token/s', + originValue: serviceOverview.avgToken, date: serviceOverview.date, - min: serviceOverview.minToken, - max: serviceOverview.maxToken, + min: formatNumberWithUnit(serviceOverview.minToken) + ' Token/s', + max: formatNumberWithUnit(serviceOverview.maxToken) + ' Token/s', type: 'area' }, - // 平均请求 - setBarChartInfoData({ - title: $t('平均每消费者的请求次数'), - data: serviceOverview.avgRequestPerSubscriberOverview, - value: serviceOverview.avgRequestPerSubscriber, - date: serviceOverview.date - }), + { + // 平均请求 + ...setBarChartInfoData({ + title: $t('平均每消费者的请求次数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + date: serviceOverview.date + }), + max: abbreviateFloat(serviceOverview.maxRequestPerSubscriber), + min: abbreviateFloat(serviceOverview.minRequestPerSubscriber) + }, // 评价 token 消耗 - setBarChartInfoData({ - title: $t('平均每消费者的 Token 消耗'), - data: serviceOverview.avgTokenPerSubscriberOverview.map((item: { inputToken: number; outputToken: number }) => ({ - inputToken: item.inputToken, - outputToken: item.outputToken - })), - value: serviceOverview.avgTokenPerSubscriber, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('平均每消费者的 Token 消耗'), + data: serviceOverview.avgTokenPerSubscriberOverview.map( + (item: { inputToken: number; outputToken: number }) => ({ + inputToken: item.inputToken, + outputToken: item.outputToken + }) + ), + date: serviceOverview.date + }), + max: abbreviateFloat(serviceOverview.maxTokenPerSubscriber), + min: abbreviateFloat(serviceOverview.minTokenPerSubscriber) + } ]) } @@ -268,7 +288,11 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ 'request_5xx_total', 'traffic_2xx_total', 'traffic_4xx_total', - 'traffic_5xx_total' + 'traffic_5xx_total', + 'max_request_per_subscriber', + 'min_request_per_subscriber', + 'max_traffic_per_subscriber', + 'min_traffic_per_subscriber' ] }).then((response) => { const { code, data, msg } = response @@ -354,7 +378,13 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ body: 'py-[15px] px-[0px]' }} > - + ))}
@@ -378,7 +408,13 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ > ) : ( - + )} ))} diff --git a/frontend/packages/core/src/pages/serviceOverview/utils.ts b/frontend/packages/core/src/pages/serviceOverview/utils.ts index 6ec42e3b..ad68a882 100644 --- a/frontend/packages/core/src/pages/serviceOverview/utils.ts +++ b/frontend/packages/core/src/pages/serviceOverview/utils.ts @@ -1,6 +1,6 @@ export type BarData = { title: string - value: string + value?: string date: string[] data: any[] showXAxis?: boolean diff --git a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx index 316b0b57..f91f46bb 100644 --- a/frontend/packages/dashboard/src/pages/DashboardTotal.tsx +++ b/frontend/packages/dashboard/src/pages/DashboardTotal.tsx @@ -4,7 +4,7 @@ import ScrollableSection from '@common/components/aoplatform/ScrollableSection' import { TimeRange } from '@common/components/aoplatform/TimeRangeSelector' import { useEffect, useState } from 'react' import DateSelectFilter, { TimeOption } from '@core/pages/serviceOverview/filter/DateSelectFilter' -import { getTime } from '@dashboard/utils/dashboard' +import { abbreviateFloat, formatBytes, formatDuration, formatNumberWithUnit, getTime } from '@dashboard/utils/dashboard' import { $t } from '@common/locales/index.ts' import { LoadingOutlined } from '@ant-design/icons' import { Card, Spin } from 'antd' @@ -71,7 +71,11 @@ export default function DashboardTotal() { 'request_4xx_total', 'request_5xx_total', 'input_token_total', - 'output_token_total' + 'output_token_total', + 'max_request_per_subscriber', + 'min_request_per_subscriber', + 'max_token_per_subscriber', + 'min_token_per_subscriber' ] }).then((response) => { const { code, data, msg } = response @@ -109,7 +113,11 @@ export default function DashboardTotal() { 'request_5xx_total', 'traffic_2xx_total', 'traffic_4xx_total', - 'traffic_5xx_total' + 'traffic_5xx_total', + 'max_request_per_subscriber', + 'min_request_per_subscriber', + 'max_traffic_per_subscriber', + 'min_traffic_per_subscriber' ] }).then((response) => { const { code, data, msg } = response @@ -135,24 +143,24 @@ export default function DashboardTotal() { ...setBarChartInfoData({ title: $t('请求次数'), data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, + value: formatNumberWithUnit(serviceOverview.requestTotal), date: serviceOverview.date }), - request2xxTotal: serviceOverview.request2xxTotal, - request4xxTotal: serviceOverview.request4xxTotal, - request5xxTotal: serviceOverview.request5xxTotal + request2xxTotal: formatNumberWithUnit(serviceOverview.request2xxTotal), + request4xxTotal: formatNumberWithUnit(serviceOverview.request4xxTotal), + request5xxTotal: formatNumberWithUnit(serviceOverview.request5xxTotal) }, // 流量消耗总数 { ...setBarChartInfoData({ title: $t('网络流量'), data: serviceOverview.trafficOverview, - value: serviceOverview.trafficTotal, + value: formatBytes(serviceOverview.trafficTotal), date: serviceOverview.date }), - traffic2xxTotal: serviceOverview.traffic2xxTotal, - traffic4xxTotal: serviceOverview.traffic4xxTotal, - traffic5xxTotal: serviceOverview.traffic5xxTotal + traffic2xxTotal: formatBytes(serviceOverview.traffic2xxTotal), + traffic4xxTotal: formatBytes(serviceOverview.traffic4xxTotal), + traffic5xxTotal: formatBytes(serviceOverview.traffic5xxTotal) } ]) // 设置平均值数据 @@ -161,26 +169,33 @@ export default function DashboardTotal() { { title: $t('平均响应时间'), data: serviceOverview.avgResponseTimeOverview, - value: serviceOverview.avgResponseTime, + value: formatDuration(serviceOverview.avgResponseTime), + originValue: serviceOverview.avgResponseTime, date: serviceOverview.date, - min: serviceOverview.minResponseTime, - max: serviceOverview.maxResponseTime, + min: formatDuration(serviceOverview.minResponseTime), + max: formatDuration(serviceOverview.maxResponseTime), type: 'area' }, // 平均请求 - setBarChartInfoData({ - title: $t('平均每消费者的请求次数'), - data: serviceOverview.avgRequestPerSubscriberOverview, - value: serviceOverview.avgRequestPerSubscriber, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('平均每消费者的请求次数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + date: serviceOverview.date + }), + max: abbreviateFloat(serviceOverview.maxRequestPerSubscriber), + min: abbreviateFloat(serviceOverview.minRequestPerSubscriber) + }, // 平均流量消耗 - setBarChartInfoData({ - title: $t('平均每消费者的网络流量'), - data: serviceOverview.avgTrafficPerSubscriberOverview, - value: serviceOverview.avgTrafficPerSubscriber, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('平均每消费者的网络流量'), + data: serviceOverview.avgTrafficPerSubscriberOverview, + date: serviceOverview.date + }), + max: formatBytes(serviceOverview.maxTrafficPerSubscriber), + min: formatBytes(serviceOverview.minTrafficPerSubscriber) + } ]) } @@ -195,12 +210,12 @@ export default function DashboardTotal() { ...setBarChartInfoData({ title: $t('请求次数'), data: serviceOverview.requestOverview, - value: serviceOverview.requestTotal, + value: formatNumberWithUnit(serviceOverview.requestTotal), date: serviceOverview.date }), - request2xxTotal: serviceOverview.request2xxTotal, - request4xxTotal: serviceOverview.request4xxTotal, - request5xxTotal: serviceOverview.request5xxTotal + request2xxTotal: formatNumberWithUnit(serviceOverview.request2xxTotal), + request4xxTotal: formatNumberWithUnit(serviceOverview.request4xxTotal), + request5xxTotal: formatNumberWithUnit(serviceOverview.request5xxTotal) }, // token 消耗总数 { @@ -210,11 +225,11 @@ export default function DashboardTotal() { inputToken: item.inputToken, outputToken: item.outputToken })), - value: serviceOverview.tokenTotal, + value: formatNumberWithUnit(serviceOverview.tokenTotal), date: serviceOverview.date }), - inputTokenTotal: serviceOverview.inputTokenTotal, - outputTokenTotal: serviceOverview.outputTokenTotal + inputTokenTotal: formatNumberWithUnit(serviceOverview.inputTokenTotal), + outputTokenTotal: formatNumberWithUnit(serviceOverview.outputTokenTotal) } ]) // 设置平均值数据 @@ -223,31 +238,38 @@ export default function DashboardTotal() { { title: $t('平均 Token 消耗'), data: serviceOverview.avgTokenOverview, - value: serviceOverview.avgToken, + value: formatNumberWithUnit(serviceOverview.avgToken) + ' Token/s', + originValue: serviceOverview.avgToken, date: serviceOverview.date, - min: serviceOverview.minToken, - max: serviceOverview.maxToken, + min: formatNumberWithUnit(serviceOverview.minToken) + ' Token/s', + max: formatNumberWithUnit(serviceOverview.maxToken) + ' Token/s', type: 'area' }, // 平均请求 - setBarChartInfoData({ - title: $t('平均每消费者的请求次数'), - data: serviceOverview.avgRequestPerSubscriberOverview, - value: serviceOverview.avgRequestPerSubscriber, - date: serviceOverview.date - }), + { + ...setBarChartInfoData({ + title: $t('平均每消费者的请求次数'), + data: serviceOverview.avgRequestPerSubscriberOverview, + date: serviceOverview.date + }), + max: abbreviateFloat(serviceOverview.maxRequestPerSubscriber), + min: abbreviateFloat(serviceOverview.minRequestPerSubscriber) + }, // 平均 token 消耗 - setBarChartInfoData({ - title: $t('平均每消费者的 Token 消耗'), - data: serviceOverview.avgTokenPerSubscriberOverview.map( - (item: { inputToken: number; outputToken: number }) => ({ - inputToken: item.inputToken, - outputToken: item.outputToken - }) - ), - value: serviceOverview.avgTokenPerSubscriber, - date: serviceOverview.date - }) + { + ...setBarChartInfoData({ + title: $t('平均每消费者的 Token 消耗'), + data: serviceOverview.avgTokenPerSubscriberOverview.map( + (item: { inputToken: number; outputToken: number }) => ({ + inputToken: item.inputToken, + outputToken: item.outputToken + }) + ), + date: serviceOverview.date + }), + max: abbreviateFloat(serviceOverview.maxTokenPerSubscriber), + min: abbreviateFloat(serviceOverview.minTokenPerSubscriber) + } ]) } @@ -394,7 +416,7 @@ export default function DashboardTotal() { diff --git a/frontend/packages/dashboard/src/utils/dashboard.ts b/frontend/packages/dashboard/src/utils/dashboard.ts index d81a0f60..58b20cb6 100644 --- a/frontend/packages/dashboard/src/utils/dashboard.ts +++ b/frontend/packages/dashboard/src/utils/dashboard.ts @@ -89,3 +89,114 @@ export function yUnitFormatter(value: number): string { } return res } + +/** + * 格式化数字并添加适当的单位后缀 + * 根据数字大小自动选择合适的单位: + * - 小于1000:原始数字 + * - 千级别(K): 1,000 - 999,999 + * - 百万级别(M): 1,000,000 - 999,999,999 + * - 十亿级别(B): 1,000,000,000 - 999,999,999,999 + * - 万亿级别(T): 1,000,000,000,000及以上 + * @param count 需要格式化的数字 + * @returns 格式化后的字符串,包含单位 + */ +export function formatNumberWithUnit(count: number) { + if (count < 1000) { + return count.toString() // 小于1000直接返回 + } else if (count < 1_000_000) { + return (count / 1000).toFixed(1) + 'K' // 千级别,如1.5K + } else if (count < 1_000_000_000) { + return (count / 1_000_000).toFixed(1) + 'M' // 百万级别,如2.3M + } else if (count < 1_000_000_000_000) { + return (count / 1_000_000_000).toFixed(1) + 'B' // 十亿级别,如4.5B + } else { + return (count / 1_000_000_000_000).toFixed(1) + 'T' // 万亿级别,如7.8T + } +} + +/** + * 格式化浮点数并缩写为带单位的形式 + * 与 formatNumberWithUnit 类似,但对小于1000的数字也进行保留1位小数的格式化 + * - 小于1000:保留1位小数 + * - 千级别(K): 1,000 - 999,999 + * - 百万级别(M): 1,000,000 - 999,999,999 + * - 十亿级别(B): 1,000,000,000 - 999,999,999,999 + * - 万亿级别(T): 1,000,000,000,000及以上 + * @param count 需要格式化的数字 + * @returns 格式化后的字符串,包含单位 + */ +export function abbreviateFloat(count: number) { + if (count < 1000) { + return count.toFixed(1) // 小于1000的数字保留1位小数,如5.0 + } else if (count < 1_000_000) { + return (count / 1000).toFixed(1) + 'K' // 千级别,如1.5K + } else if (count < 1_000_000_000) { + return (count / 1_000_000).toFixed(1) + 'M' // 百万级别,如2.3M + } else if (count < 1_000_000_000_000) { + return (count / 1_000_000_000).toFixed(1) + 'B' // 十亿级别,如4.5B + } else { + return (count / 1_000_000_000_000).toFixed(1) + 'T' // 万亿级别,如7.8T + } +} + +/** + * 格式化时间持续时间,自动选择合适的时间单位 + * 根据持续时间的长度自动选择不同的单位: + * - 毫秒(ms): < 1000 + * - 秒(s): 1,000 - 999,999 + * - 分钟(min): 1,000,000 - 999,999,999 + * - 小时(hour): 1,000,000,000 - 999,999,999,999 + * - 天(day): ≥ 1,000,000,000,000 + * @param durationNano 时间持续时间数值 + * @returns 格式化后的字符串,包含单位 + */ +export function formatDuration(durationNano: number) { + if (durationNano < 1000) { + return `${durationNano}ms` // 小于1000,返回毫秒 + } + if (durationNano < 1_000_000) { + return (durationNano / 1000).toFixed(1) + 's' // 转换为秒 + } + if (durationNano < 1_000_000_000) { + return (durationNano / 1_000_000).toFixed(1) + 'min' // 转换为分钟 + } + if (durationNano < 1_000_000_000_000) { + return (durationNano / 1_000_000_000).toFixed(1) + 'hour' // 转换为小时 + } + return (durationNano / 1_000_000_000_000).toFixed(1) + 'day' // 转换为天 +} + +/** + * 格式化数据大小,自动选择合适的单位 + * 根据字节数自动选择适当的单位进行显示: + * - 字节(B): < 1000 + * - 千字节(KB): 1,000 - 999,999 + * - 兆字节(MB): 1,000,000 - 999,999,999 + * - 吉字节(GB): 1,000,000,000 - 999,999,999,999 + * - 太字节(TB): 1,000,000,000,000 - 999,999,999,999,999 + * - 拉字节(PB): ≥ 1,000,000,000,000,000 + * @param bytes 字节数 + * @returns 格式化后的字符串,包含单位 + */ +export function formatBytes(bytes: number) { + const KB = 1000 // 千字节 + const MB = KB * 1000 // 兆字节 + const GB = MB * 1000 // 吉字节 + const TB = GB * 1000 // 太字节 + const PB = TB * 1000 // 拉字节 + + if (bytes < KB) { + return `${bytes}B` // 直接显示字节 + } else if (bytes < MB) { + return (bytes / KB).toFixed(1) + 'KB' // 转换为千字节 + } else if (bytes < GB) { + return (bytes / MB).toFixed(1) + 'MB' // 转换为兆字节 + } else if (bytes < TB) { + return (bytes / GB).toFixed(1) + 'GB' // 转换为吉字节 + } else if (bytes < PB) { + return (bytes / TB).toFixed(1) + 'TB' // 转换为太字节 + } else { + return (bytes / PB).toFixed(1) + 'PB' // 转换为拉字节 + } +} From 83da8df55455c85648caee6f32f44e2853950f1d Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 6 May 2025 14:40:32 +0800 Subject: [PATCH 27/29] feature/1.8-Improve system observability --- .../aoplatform/TimeRangeSelector.tsx | 4 ++ .../src/component/MonitorApiPage.tsx | 37 ++++++++++--------- .../src/component/MonitorAppPage.tsx | 30 +++++++++------ .../src/component/MonitorDetailPage.tsx | 29 +++++++++------ .../src/component/MonitorSubPage.tsx | 30 +++++++++------ 5 files changed, 78 insertions(+), 52 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx b/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx index 56dbba6e..6df2e13c 100644 --- a/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx +++ b/frontend/packages/common/src/components/aoplatform/TimeRangeSelector.tsx @@ -112,6 +112,10 @@ const TimeRangeSelector = (props: TimeRangeSelectorProps) => { return current && current.valueOf() > dayjs().startOf('day').valueOf() } + useEffect(() => { + setTimeButton(initialTimeButton || '') + }, [initialTimeButton]) + return (
{!hideTitle && } diff --git a/frontend/packages/dashboard/src/component/MonitorApiPage.tsx b/frontend/packages/dashboard/src/component/MonitorApiPage.tsx index 60b991ba..80995086 100644 --- a/frontend/packages/dashboard/src/component/MonitorApiPage.tsx +++ b/frontend/packages/dashboard/src/component/MonitorApiPage.tsx @@ -62,6 +62,17 @@ export default function MonitorApiPage(props: MonitorApiPageProps) { getProjectList() }, []) + /** + * 重置时间范围 + */ + let resetTimeRange = () => {} + /** + * 绑定时间范围组件 + * @param instance + */ + const bindRef = (instance: any) => { + resetTimeRange = instance.reset + } const getApiList = (projectIds?: string[]) => { return fetchData<{ apis: EntityItem[] }>('simple/service/apis', { method: 'POST', @@ -146,6 +157,7 @@ export default function MonitorApiPage(props: MonitorApiPageProps) { } const clearSearch = () => { + resetTimeRange() setTimeButton('hour') setDatePickerValue(null) setQueryData(undefined) @@ -186,11 +198,17 @@ export default function MonitorApiPage(props: MonitorApiPageProps) { setDrawerOpen(true) } + useEffect(() => { + setQueryBtnLoading(true) + getApiTableList() + }, [queryData]) + return (
{/* setQueryData({ ...queryData, path: '' })} /> */} - debounce((e) => { - setQueryData((prevData) => ({ ...(prevData || {}), path: e.target.value })) - }, 100)(e) - } + value={queryData?.path || ''} + onChange={(e) => setQueryData((prevData) => ({ ...(prevData || {}), path: e.target.value }))} allowClear placeholder={$t('请输入请求路径进行搜索')} prefix={} @@ -249,17 +263,6 @@ export default function MonitorApiPage(props: MonitorApiPageProps) { - diff --git a/frontend/packages/dashboard/src/component/MonitorAppPage.tsx b/frontend/packages/dashboard/src/component/MonitorAppPage.tsx index 55667089..7df1c67e 100644 --- a/frontend/packages/dashboard/src/component/MonitorAppPage.tsx +++ b/frontend/packages/dashboard/src/component/MonitorAppPage.tsx @@ -58,7 +58,17 @@ export default function MonitorAppPage(props: MonitorAppPageProps) { getMonitorData() getAppList() }, []) - + /** + * 重置时间范围 + */ + let resetTimeRange = () => {} + /** + * 绑定时间范围组件 + * @param instance + */ + const bindRef = (instance: any) => { + resetTimeRange = instance.reset + } const getMonitorData = () => { let query = queryData if (!queryData || queryData.start === undefined) { @@ -86,6 +96,7 @@ export default function MonitorAppPage(props: MonitorAppPageProps) { } const clearSearch = () => { + resetTimeRange() setTimeButton('hour') setDatePickerValue(null) setQueryData({ type: 'subscriber' }) @@ -163,10 +174,16 @@ export default function MonitorAppPage(props: MonitorAppPageProps) { setDrawerOpen(true) } + useEffect(() => { + setQueryBtnLoading(true) + getAppTableList() + }, [queryData]) + return (
{$t('重置')} - diff --git a/frontend/packages/dashboard/src/component/MonitorDetailPage.tsx b/frontend/packages/dashboard/src/component/MonitorDetailPage.tsx index 4b0e2c73..55756ca8 100644 --- a/frontend/packages/dashboard/src/component/MonitorDetailPage.tsx +++ b/frontend/packages/dashboard/src/component/MonitorDetailPage.tsx @@ -78,7 +78,17 @@ export default function MonitorDetailPage(props: MonitorDetailPageProps) { const monitorTableRef = useRef(null) const [modalTitle, setModalTitle] = useState($t('调用趋势')) const [queryBtnLoading, setQueryBtnLoading] = useState(false) - + /** + * 重置时间范围 + */ + let resetTimeRange = () => {} + /** + * 绑定时间范围组件 + * @param instance + */ + const bindRef = (instance: any) => { + resetTimeRange = instance.reset + } useEffect(() => { // 初始化数据 getMonitorData() @@ -144,6 +154,7 @@ export default function MonitorDetailPage(props: MonitorDetailPageProps) { } const clearSearch = () => { + resetTimeRange() setTimeButton('hour') setDatePickerValue(null) setQueryData(null) @@ -178,11 +189,17 @@ export default function MonitorDetailPage(props: MonitorDetailPageProps) { setQueryData((pre) => ({ ...pre, ...timeRange }) as SearchBody) } + useEffect(() => { + setQueryBtnLoading(true) + getMonitorData() + }, [queryData]) + return (
{$t('重置')} -
diff --git a/frontend/packages/dashboard/src/component/MonitorSubPage.tsx b/frontend/packages/dashboard/src/component/MonitorSubPage.tsx index c400b52d..82a7f137 100644 --- a/frontend/packages/dashboard/src/component/MonitorSubPage.tsx +++ b/frontend/packages/dashboard/src/component/MonitorSubPage.tsx @@ -63,7 +63,17 @@ export default function MonitorSubPage(props: MonitorSubPageProps) { getMonitorData() getProjectList() }, []) - + /** + * 重置时间范围 + */ + let resetTimeRange = () => {} + /** + * 绑定时间范围组件 + * @param instance + */ + const bindRef = (instance: any) => { + resetTimeRange = instance.reset + } const getMonitorData = () => { let query = queryData if (!queryData || queryData.start === undefined) { @@ -91,6 +101,7 @@ export default function MonitorSubPage(props: MonitorSubPageProps) { } const clearSearch = () => { + resetTimeRange() setTimeButton('hour') setDatePickerValue(null) setQueryData({ type: 'provider' }) @@ -168,10 +179,16 @@ export default function MonitorSubPage(props: MonitorSubPageProps) { setDrawerOpen(true) } + useEffect(() => { + setQueryBtnLoading(true) + getAppTableList() + }, [queryData]) + return (
{$t('重置')} - From 304239edd047429fd306fda8b9aa4ad9521303e8 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 6 May 2025 16:06:00 +0800 Subject: [PATCH 28/29] feature/1.8-Improve system observability --- .../core/src/pages/keySettings/components/ApiKeyContent.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx b/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx index 29338962..5a586ede 100644 --- a/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx +++ b/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx @@ -39,6 +39,7 @@ const ApiKeyContent: React.FC = forwardRef(({ provider, enti const handleOk = async () => { try { + // 表单校验 const values = await form.validateFields() const { expire_time, ...restValues } = values const expireTime = neverExpire ? 0 : Math.trunc(expire_time.valueOf() / 1000) From a0f4ef4e19f9f68c220b5e2773bd52c02252bd6c Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 6 May 2025 17:57:56 +0800 Subject: [PATCH 29/29] feat: feature/1.8-Improve system observability --- frontend/packages/common/src/locales/scan/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index 087cb301..a8502918 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -883,7 +883,7 @@ "Kaf9e8011": "总览", "Ke1e649cb": "服务尚未发布", "K2e683a7d": "跳转至详情页", - "Ke04bc00d": "订阅数量", + "Ke04bc00d": "订阅方数量", "K1b97ae0a": "已开启", "K19ec733b": "开启 MCP", "Kbee2340": "API 使用排名",