feat(admin): 添加网络代理配置和钱包交易功能

- 在管理面板中集成网络代理配置显示和平台代理设置
- 添加钱包摘要和交易列表API接口及数据管理
- 实现SSE流式响应中的错误处理机制
- 添加全局HTTP代理环境变量配置支持
- 更新平台表单以支持代理模式选择和自定义代理地址
- 集成钱包交易查询过滤和分页功能
- 优化API错误详情解析和显示格式
This commit is contained in:
2026-05-11 23:02:10 +08:00
parent c992f1de60
commit f550c0acd5
30 changed files with 1455 additions and 76 deletions
+37 -1
View File
@@ -47,6 +47,32 @@ export interface MediaGenerationRun {
task?: GatewayTask;
}
export function gatewayTaskErrorText(task: GatewayTask | undefined, fallback = '任务失败') {
if (!task) return fallback;
const result = recordFromUnknown(task.result);
const resultError = recordFromUnknown(result?.error);
const metrics = recordFromUnknown(task.metrics);
const hasErrorState = task.status === 'failed'
|| task.status === 'cancelled'
|| Boolean(task.error || task.errorMessage || task.errorCode || resultError || metrics?.error);
if (!hasErrorState) return '';
const message = firstString(
task.errorMessage,
task.error,
resultError?.message,
result?.message,
metrics?.error,
fallback,
);
const meta = uniqueStrings([
firstString(task.errorCode, resultError?.code, metrics?.errorCode) ? `错误码: ${firstString(task.errorCode, resultError?.code, metrics?.errorCode)}` : '',
firstString(task.requestId, resultError?.requestId, resultError?.request_id, metrics?.requestId) ? `RequestID: ${firstString(task.requestId, resultError?.requestId, resultError?.request_id, metrics?.requestId)}` : '',
task.id ? `TaskID: ${task.id}` : '',
]);
if (!message && !meta.length) return '';
return meta.length ? `${message || fallback}${meta.join('')}` : message;
}
interface AspectRatioOption {
label: string;
value: string;
@@ -487,6 +513,7 @@ function MediaTaskCard(props: {
const unit = props.run.mode === 'video' ? '条' : '张';
const isPending = props.run.status === 'submitting' || props.run.status === 'queued' || props.run.status === 'running';
const backdropItem = expectedCount === 1 && items[0]?.type === 'image' ? items[0] : undefined;
const errorText = mediaRunErrorText(props.run);
return (
<article className="mediaTaskItem" data-status={props.run.status}>
@@ -517,7 +544,12 @@ function MediaTaskCard(props: {
</div>
</div>
{props.run.error && <p className="mediaTaskError">{props.run.error}</p>}
{errorText && (
<div className="mediaTaskError">
<strong></strong>
<span>{errorText}</span>
</div>
)}
<footer className="mediaTaskActions">
{items[0] ? (
<Button asChild size="sm" variant="secondary">
@@ -593,6 +625,10 @@ function mediaResultItems(run: MediaGenerationRun): MediaResultItem[] {
.filter((item): item is MediaResultItem => Boolean(item));
}
function mediaRunErrorText(run: MediaGenerationRun) {
return gatewayTaskErrorText(run.task, '') || run.error || '';
}
function mediaResultItemFromEntry(entry: unknown, mode: Exclude<PlaygroundMode, 'chat'>): MediaResultItem | undefined {
const record = recordFromUnknown(entry);
if (!record) return undefined;