feat(usage): add test script API with refactored execution logic
- Add private helper method `execute_and_format_usage_result` to eliminate code duplication - Refactor `query_usage` to use helper method instead of duplicating result processing - Add new `test_usage_script` method to test temporary script without saving - Add backend command `test_usage_script` accepting script content as parameter - Register new command in lib.rs invoke_handler - Add frontend `usageApi.testScript` method to call the new backend API - Update `UsageScriptModal.handleTest` to test current editor content instead of saved script - Improve DX: users can now test script changes before saving
This commit is contained in:
@@ -121,6 +121,35 @@ pub async fn query_provider_usage(
|
|||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 测试用量脚本(使用当前编辑器中的脚本,不保存)
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn test_usage_script(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
providerId: String,
|
||||||
|
app: String,
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
scriptCode: String,
|
||||||
|
timeout: Option<u64>,
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
accessToken: Option<String>,
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
userId: Option<String>,
|
||||||
|
) -> Result<crate::provider::UsageResult, String> {
|
||||||
|
let app_type = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||||||
|
ProviderService::test_usage_script(
|
||||||
|
state.inner(),
|
||||||
|
app_type,
|
||||||
|
&providerId,
|
||||||
|
&scriptCode,
|
||||||
|
timeout.unwrap_or(10),
|
||||||
|
accessToken.as_deref(),
|
||||||
|
userId.as_deref(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// 读取当前生效的配置内容
|
/// 读取当前生效的配置内容
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn read_live_provider_settings(app: String) -> Result<serde_json::Value, String> {
|
pub fn read_live_provider_settings(app: String) -> Result<serde_json::Value, String> {
|
||||||
|
|||||||
@@ -540,6 +540,7 @@ pub fn run() {
|
|||||||
commands::validate_mcp_command,
|
commands::validate_mcp_command,
|
||||||
// usage query
|
// usage query
|
||||||
commands::query_provider_usage,
|
commands::query_provider_usage,
|
||||||
|
commands::test_usage_script,
|
||||||
// New MCP via config.json (SSOT)
|
// New MCP via config.json (SSOT)
|
||||||
commands::get_mcp_config,
|
commands::get_mcp_config,
|
||||||
commands::upsert_mcp_server_in_config,
|
commands::upsert_mcp_server_in_config,
|
||||||
|
|||||||
@@ -706,7 +706,50 @@ impl ProviderService {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 查询供应商用量
|
/// 执行用量脚本并格式化结果(私有辅助方法)
|
||||||
|
async fn execute_and_format_usage_result(
|
||||||
|
script_code: &str,
|
||||||
|
api_key: &str,
|
||||||
|
base_url: &str,
|
||||||
|
timeout: u64,
|
||||||
|
access_token: Option<&str>,
|
||||||
|
user_id: Option<&str>,
|
||||||
|
) -> Result<UsageResult, AppError> {
|
||||||
|
match usage_script::execute_usage_script(
|
||||||
|
script_code,
|
||||||
|
api_key,
|
||||||
|
base_url,
|
||||||
|
timeout,
|
||||||
|
access_token,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(data) => {
|
||||||
|
let usage_list: Vec<UsageData> = if data.is_array() {
|
||||||
|
serde_json::from_value(data)
|
||||||
|
.map_err(|e| AppError::Message(format!("数据格式错误: {}", e)))?
|
||||||
|
} else {
|
||||||
|
let single: UsageData = serde_json::from_value(data)
|
||||||
|
.map_err(|e| AppError::Message(format!("数据格式错误: {}", e)))?;
|
||||||
|
vec![single]
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(UsageResult {
|
||||||
|
success: true,
|
||||||
|
data: Some(usage_list),
|
||||||
|
error: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => Ok(UsageResult {
|
||||||
|
success: false,
|
||||||
|
data: None,
|
||||||
|
error: Some(err.to_string()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询供应商用量(使用已保存的脚本配置)
|
||||||
pub async fn query_usage(
|
pub async fn query_usage(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
app_type: AppType,
|
app_type: AppType,
|
||||||
@@ -754,7 +797,7 @@ impl ProviderService {
|
|||||||
|
|
||||||
let (api_key, base_url) = Self::extract_credentials(&provider, &app_type)?;
|
let (api_key, base_url) = Self::extract_credentials(&provider, &app_type)?;
|
||||||
|
|
||||||
match usage_script::execute_usage_script(
|
Self::execute_and_format_usage_result(
|
||||||
&script_code,
|
&script_code,
|
||||||
&api_key,
|
&api_key,
|
||||||
&base_url,
|
&base_url,
|
||||||
@@ -763,29 +806,42 @@ impl ProviderService {
|
|||||||
user_id.as_deref(),
|
user_id.as_deref(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
}
|
||||||
Ok(data) => {
|
|
||||||
let usage_list: Vec<UsageData> = if data.is_array() {
|
|
||||||
serde_json::from_value(data)
|
|
||||||
.map_err(|e| AppError::Message(format!("数据格式错误: {}", e)))?
|
|
||||||
} else {
|
|
||||||
let single: UsageData = serde_json::from_value(data)
|
|
||||||
.map_err(|e| AppError::Message(format!("数据格式错误: {}", e)))?;
|
|
||||||
vec![single]
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(UsageResult {
|
/// 测试用量脚本(使用临时脚本内容,不保存)
|
||||||
success: true,
|
pub async fn test_usage_script(
|
||||||
data: Some(usage_list),
|
state: &AppState,
|
||||||
error: None,
|
app_type: AppType,
|
||||||
})
|
provider_id: &str,
|
||||||
}
|
script_code: &str,
|
||||||
Err(err) => Ok(UsageResult {
|
timeout: u64,
|
||||||
success: false,
|
access_token: Option<&str>,
|
||||||
data: None,
|
user_id: Option<&str>,
|
||||||
error: Some(err.to_string()),
|
) -> Result<UsageResult, AppError> {
|
||||||
}),
|
// 获取 provider 的 API 凭证
|
||||||
}
|
let provider = {
|
||||||
|
let config = state.config.read().map_err(AppError::from)?;
|
||||||
|
let manager = config
|
||||||
|
.get_manager(&app_type)
|
||||||
|
.ok_or_else(|| Self::app_not_found(&app_type))?;
|
||||||
|
manager
|
||||||
|
.providers
|
||||||
|
.get(provider_id)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| AppError::ProviderNotFound(provider_id.to_string()))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let (api_key, base_url) = Self::extract_credentials(&provider, &app_type)?;
|
||||||
|
|
||||||
|
Self::execute_and_format_usage_result(
|
||||||
|
script_code,
|
||||||
|
&api_key,
|
||||||
|
&base_url,
|
||||||
|
timeout,
|
||||||
|
access_token,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 切换指定应用的供应商
|
/// 切换指定应用的供应商
|
||||||
|
|||||||
@@ -144,7 +144,15 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
const handleTest = async () => {
|
const handleTest = async () => {
|
||||||
setTesting(true);
|
setTesting(true);
|
||||||
try {
|
try {
|
||||||
const result = await usageApi.query(provider.id, appId);
|
// 使用当前编辑器中的脚本内容进行测试
|
||||||
|
const result = await usageApi.testScript(
|
||||||
|
provider.id,
|
||||||
|
appId,
|
||||||
|
script.code,
|
||||||
|
script.timeout,
|
||||||
|
script.accessToken,
|
||||||
|
script.userId
|
||||||
|
);
|
||||||
if (result.success && result.data && result.data.length > 0) {
|
if (result.success && result.data && result.data.length > 0) {
|
||||||
// 显示所有套餐数据
|
// 显示所有套餐数据
|
||||||
const summary = result.data
|
const summary = result.data
|
||||||
|
|||||||
@@ -26,4 +26,37 @@ export const usageApi = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async testScript(
|
||||||
|
providerId: string,
|
||||||
|
appId: AppId,
|
||||||
|
scriptCode: string,
|
||||||
|
timeout?: number,
|
||||||
|
accessToken?: string,
|
||||||
|
userId?: string
|
||||||
|
): Promise<UsageResult> {
|
||||||
|
try {
|
||||||
|
return await invoke("test_usage_script", {
|
||||||
|
providerId: providerId,
|
||||||
|
app: appId,
|
||||||
|
scriptCode: scriptCode,
|
||||||
|
timeout: timeout,
|
||||||
|
accessToken: accessToken,
|
||||||
|
userId: userId,
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const message =
|
||||||
|
typeof error === "string"
|
||||||
|
? error
|
||||||
|
: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: message || i18n.t("errors.usage_query_failed"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user