1 line
135 KiB
JSON
1 line
135 KiB
JSON
{"repo": "alanyn/flowflex", "files": {"packages/flowFlex-backend/WebApi/Controllers/AI/AIConfigController.cs": {"ecbc3f24aaa127ce492afca3fd8321df5bee15df": {"t": 1785746209.8304067, "n": 13498, "h": "9492069f5ef1097b", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService,\n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI. GPT-4o is the flagship model with 128K context. Default uses Item Gateway (prefix required: openai/model-name). For official API, use model name directly.\",\n Website = \"https://openai.com\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"gpt-4o\",\n \"gpt-4o-mini\",\n \"gpt-4-turbo\",\n \"gpt-4\",\n \"gpt-3.5-turbo\",\n \"o1-preview\",\n \"o1-mini\"\n }\n },\n\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Gemini\",\n Icon = \"💎\",\n Description = \"Google's advanced multimodal AI models with strong reasoning capabilities. Default uses Item Gateway (prefix required: gemini/model-name).\",\n Website = \"https://ai.google.dev\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"gemini-2.5-flash\",\n \"gemini-2.5-pro\",\n \"gemini-2.0-flash-exp\",\n \"gemini-1.5-pro\",\n \"gemini-1.5-flash\"\n }\n },\n\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Cost-effective models specialized in code generation and reasoning. DeepSeek-V3 offers GPT-4 level performance at lower cost.\",\n Website = \"https://deepseek.com\",\n DefaultBaseUrl = \"https://api.deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-reasoner\", \"deepseek-coder\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 默认BaseURL地址\n /// </summary>\n public string DefaultBaseUrl { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "dd177a807c84d7ffd2ec453142af773d6da5c5ed": {"t": 1785760645.3567276, "n": 13498, "h": "9492069f5ef1097b", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService,\n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI. GPT-4o is the flagship model with 128K context. Default uses Item Gateway (prefix required: openai/model-name). For official API, use model name directly.\",\n Website = \"https://openai.com\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"gpt-4o\",\n \"gpt-4o-mini\",\n \"gpt-4-turbo\",\n \"gpt-4\",\n \"gpt-3.5-turbo\",\n \"o1-preview\",\n \"o1-mini\"\n }\n },\n\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Gemini\",\n Icon = \"💎\",\n Description = \"Google's advanced multimodal AI models with strong reasoning capabilities. Default uses Item Gateway (prefix required: gemini/model-name).\",\n Website = \"https://ai.google.dev\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"gemini-2.5-flash\",\n \"gemini-2.5-pro\",\n \"gemini-2.0-flash-exp\",\n \"gemini-1.5-pro\",\n \"gemini-1.5-flash\"\n }\n },\n\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Cost-effective models specialized in code generation and reasoning. DeepSeek-V3 offers GPT-4 level performance at lower cost.\",\n Website = \"https://deepseek.com\",\n DefaultBaseUrl = \"https://api.deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-reasoner\", \"deepseek-coder\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 默认BaseURL地址\n /// </summary>\n public string DefaultBaseUrl { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "1a4fb40bdbd9371395e22a4481af18780a4ccc23": {"t": 1785760646.876962, "n": 13506, "h": "dec5ddb97a61950e", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService,\n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI. GPT-4o is the flagship model with 128K context. Default uses Item Gateway (prefix required: openai/model-name). For official API, use model name directly.\",\n Website = \"https://openai.com\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] { \n \"gpt-4o\", \n \"gpt-4o-mini\", \n \"gpt-4-turbo\", \n \"gpt-4\", \n \"gpt-3.5-turbo\", \n \"o1-preview\", \n \"o1-mini\" \n }\n },\n\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Gemini\",\n Icon = \"💎\",\n Description = \"Google's advanced multimodal AI models with strong reasoning capabilities. Default uses Item Gateway (prefix required: gemini/model-name).\",\n Website = \"https://ai.google.dev\",\n DefaultBaseUrl = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"gemini-2.5-flash\",\n \"gemini-2.5-pro\",\n \"gemini-2.0-flash-exp\",\n \"gemini-1.5-pro\",\n \"gemini-1.5-flash\"\n }\n },\n\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Cost-effective models specialized in code generation and reasoning. DeepSeek-V3 offers GPT-4 level performance at lower cost.\",\n Website = \"https://deepseek.com\",\n DefaultBaseUrl = \"https://api.deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-reasoner\", \"deepseek-coder\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 默认BaseURL地址\n /// </summary>\n public string DefaultBaseUrl { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "88298d0724615d070dcaea61ced09e5e10dbbbc4": {"t": 1785760648.2529345, "n": 13265, "h": "9739b60865e28e2f", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService,\n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI. GPT-4o is the flagship model with 128K context. BaseURL example: https://api.openai.com/v1\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4o\", \"gpt-4o-mini\", \"gpt-4-turbo\", \"gpt-4\", \"gpt-3.5-turbo\", \"o1-preview\", \"o1-mini\" }\n },\n\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Cost-effective models specialized in code generation and reasoning. DeepSeek-V3 offers GPT-4 level performance at lower cost. BaseURL example: https://api.deepseek.com\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-reasoner\", \"deepseek-coder\" }\n },\n new AIProviderInfo\n {\n Name = \"item\",\n DisplayName = \"Item (OpenAI/Gemini)\",\n Icon = \"🏢\",\n Description = \"Company internal LLM Gateway service supporting multiple AI providers. Unified access to OpenAI and Gemini models. BaseURL example: https://aiop-gateway.item.com (default). API Key format: gw-wfe-{env}-{id}\",\n Website = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] {\n \"openai/gpt-4o\",\n \"openai/gpt-4o-mini\",\n \"openai/gpt-4-turbo\",\n \"openai/gpt-4\",\n \"openai/gpt-3.5-turbo\",\n \"gemini/gemini-2.5-flash\",\n \"gemini/gemini-2.5-pro\",\n \"gemini/gemini-2.0-flash-exp\",\n \"gemini/gemini-1.5-pro\",\n \"gemini/gemini-1.5-flash\"\n }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "7725ddf8c389c0c4bca2154effb6abebec351563": {"t": 1785760649.6459403, "n": 13316, "h": "7c0468ab35b52271", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService, \n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI. GPT-4o is the flagship model with 128K context. BaseURL example: https://api.openai.com/v1\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4o\", \"gpt-4o-mini\", \"gpt-4-turbo\", \"gpt-4\", \"gpt-3.5-turbo\", \"o1-preview\", \"o1-mini\" }\n },\n \n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Cost-effective models specialized in code generation and reasoning. DeepSeek-V3 offers GPT-4 level performance at lower cost. BaseURL example: https://api.deepseek.com\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-reasoner\", \"deepseek-coder\" }\n },\n new AIProviderInfo\n {\n Name = \"item\",\n DisplayName = \"Item (OpenAI/Gemini)\",\n Icon = \"🏢\",\n Description = \"Company internal LLM Gateway service supporting multiple AI providers. Unified access to OpenAI and Gemini models. BaseURL example: https://aiop-gateway.item.com (default). API Key format: gw-wfe-{env}-{id}\",\n Website = \"https://aiop-gateway.item.com\",\n SupportedModels = new[] { \n \"openai/gpt-4o\", \n \"openai/gpt-4o-mini\", \n \"openai/gpt-4-turbo\", \n \"openai/gpt-4\", \n \"openai/gpt-3.5-turbo\", \n \"gemini/gemini-2.5-flash\", \n \"gemini/gemini-2.5-pro\", \n \"gemini/gemini-2.0-flash-exp\", \n \"gemini/gemini-1.5-pro\", \n \"gemini/gemini-1.5-flash\" \n }\n } \n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "26a68c3153db05669faf531768b30b5fead7b9e3": {"t": 1785760651.1672606, "n": 16583, "h": "810987c68e80c64b", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing FlowFlex.Application.Contracts.IServices.OW;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n private readonly IOperatorContextService _operatorContextService;\n\n public AIConfigController(\n IAIModelConfigService configService, \n ILogger<AIConfigController> logger,\n IOperatorContextService operatorContextService)\n {\n _configService = configService;\n _logger = logger;\n _operatorContextService = operatorContextService;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId; // 保留用于审计追踪\n config.CreatedBy = operatorId;\n config.CreatedByName = operatorName;\n config.CreatedTime = currentTime;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n var operatorId = _operatorContextService.GetOperatorId();\n var operatorName = _operatorContextService.GetOperatorDisplayName();\n var currentTime = DateTime.UtcNow;\n\n config.UserId = operatorId;\n config.UpdatedBy = operatorId;\n config.UpdatedByName = operatorName;\n config.UpdatedTime = currentTime;\n\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI, including GPT-4 and GPT-3.5. BaseURL example: https://api.openai.com/v1\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4\", \"gpt-4-turbo\", \"gpt-4o\", \"gpt-4o-mini\", \"gpt-3.5-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"claude\",\n DisplayName = \"Claude (Anthropic)\",\n Icon = \"🎭\",\n Description = \"Advanced AI models by Anthropic, focusing on safety and helpfulness. BaseURL example: https://api.anthropic.com\",\n Website = \"https://claude.ai\",\n SupportedModels = new[] { \"claude-3-opus-20240229\", \"claude-3-sonnet-20240229\", \"claude-3-haiku-20240307\", \"claude-3-5-sonnet-20241022\" }\n },\n new AIProviderInfo\n {\n Name = \"zhipuai\",\n DisplayName = \"ZhipuAI (GLM)\",\n Icon = \"🤖\",\n Description = \"Chinese AI models from ZhipuAI, supporting text generation and conversation. BaseURL example: https://open.bigmodel.cn/api/paas/v4\",\n Website = \"https://zhipuai.cn\",\n SupportedModels = new[] { \"glm-4\", \"glm-4v\", \"glm-4-plus\", \"glm-3-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Specialized in code generation and mathematical reasoning. BaseURL example: https://api.deepseek.com/v1\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-coder\", \"deepseek-math\" }\n },\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Google Gemini (coming soon)\",\n Icon = \"💎\",\n Description = \"Google's multimodal AI models with advanced reasoning capabilities. BaseURL example: https://generativelanguage.googleapis.com/v1beta\",\n Website = \"https://ai.google.dev\",\n SupportedModels = new[] { \"gemini-pro\", \"gemini-pro-vision\", \"gemini-ultra (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"mistral\",\n DisplayName = \"Mistral AI (coming soon)\",\n Icon = \"🌪️\",\n Description = \"European AI company providing efficient and powerful language models. BaseURL example: https://api.mistral.ai/v1\",\n Website = \"https://mistral.ai\",\n SupportedModels = new[] { \"mistral-large\", \"mistral-medium\", \"mistral-small (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"cohere\",\n DisplayName = \"Cohere (coming soon)\",\n Icon = \"🧠\",\n Description = \"Enterprise-focused language models with strong multilingual capabilities. BaseURL example: https://api.cohere.ai/v1\",\n Website = \"https://cohere.com\",\n SupportedModels = new[] { \"command\", \"command-light\", \"command-nightly (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"qwen\",\n DisplayName = \"Alibaba Qwen (coming soon)\",\n Icon = \"☁️\",\n Description = \"Alibaba's Qwen series models with strong Chinese and English capabilities. BaseURL example: https://dashscope.aliyuncs.com/api/v1\",\n Website = \"https://tongyi.aliyun.com\",\n SupportedModels = new[] { \"qwen-turbo\", \"qwen-plus\", \"qwen-max (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"baidu\",\n DisplayName = \"Baidu ERNIE (coming soon)\",\n Icon = \"🐻\",\n Description = \"Baidu's ERNIE series models optimized for Chinese language understanding. BaseURL example: https://aip.baidubce.com/rpc/2.0/ai_custom/v1\",\n Website = \"https://cloud.baidu.com/product/wenxinworkshop\",\n SupportedModels = new[] { \"ernie-bot\", \"ernie-bot-turbo\", \"ernie-bot-4 (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"moonshot\",\n DisplayName = \"Moonshot AI (coming soon)\",\n Icon = \"🌙\",\n Description = \"Kimi models with exceptional long-context capabilities up to 2M tokens. BaseURL example: https://api.moonshot.cn/v1\",\n Website = \"https://kimi.moonshot.cn\",\n SupportedModels = new[] { \"moonshot-v1-8k\", \"moonshot-v1-32k\", \"moonshot-v1-128k (coming soon)\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "71529fc5ce7146f9257d150c5843a8a2ee5bfa00": {"t": 1785760654.5902057, "n": 15452, "h": "18afb4c3feba3ed8", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n\n public AIConfigController(IAIModelConfigService configService, ILogger<AIConfigController> logger)\n {\n _configService = configService;\n _logger = logger;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n config.UserId = GetCurrentUserId(); // 保留用于审计追踪\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n config.UserId = GetCurrentUserId();\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n\n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\",\n config.Id, updatedConfig.IsAvailable);\n }\n }\n\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI, including GPT-4 and GPT-3.5. BaseURL example: https://api.openai.com/v1\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4\", \"gpt-4-turbo\", \"gpt-4o\", \"gpt-4o-mini\", \"gpt-3.5-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"claude\",\n DisplayName = \"Claude (Anthropic)\",\n Icon = \"🎭\",\n Description = \"Advanced AI models by Anthropic, focusing on safety and helpfulness. BaseURL example: https://api.anthropic.com\",\n Website = \"https://claude.ai\",\n SupportedModels = new[] { \"claude-3-opus-20240229\", \"claude-3-sonnet-20240229\", \"claude-3-haiku-20240307\", \"claude-3-5-sonnet-20241022\" }\n },\n new AIProviderInfo\n {\n Name = \"zhipuai\",\n DisplayName = \"ZhipuAI (GLM)\",\n Icon = \"🤖\",\n Description = \"Chinese AI models from ZhipuAI, supporting text generation and conversation. BaseURL example: https://open.bigmodel.cn/api/paas/v4\",\n Website = \"https://zhipuai.cn\",\n SupportedModels = new[] { \"glm-4\", \"glm-4v\", \"glm-4-plus\", \"glm-3-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Specialized in code generation and mathematical reasoning. BaseURL example: https://api.deepseek.com/v1\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-coder\", \"deepseek-math\" }\n },\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Google Gemini (coming soon)\",\n Icon = \"💎\",\n Description = \"Google's multimodal AI models with advanced reasoning capabilities. BaseURL example: https://generativelanguage.googleapis.com/v1beta\",\n Website = \"https://ai.google.dev\",\n SupportedModels = new[] { \"gemini-pro\", \"gemini-pro-vision\", \"gemini-ultra (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"mistral\",\n DisplayName = \"Mistral AI (coming soon)\",\n Icon = \"🌪️\",\n Description = \"European AI company providing efficient and powerful language models. BaseURL example: https://api.mistral.ai/v1\",\n Website = \"https://mistral.ai\",\n SupportedModels = new[] { \"mistral-large\", \"mistral-medium\", \"mistral-small (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"cohere\",\n DisplayName = \"Cohere (coming soon)\",\n Icon = \"🧠\",\n Description = \"Enterprise-focused language models with strong multilingual capabilities. BaseURL example: https://api.cohere.ai/v1\",\n Website = \"https://cohere.com\",\n SupportedModels = new[] { \"command\", \"command-light\", \"command-nightly (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"qwen\",\n DisplayName = \"Alibaba Qwen (coming soon)\",\n Icon = \"☁️\",\n Description = \"Alibaba's Qwen series models with strong Chinese and English capabilities. BaseURL example: https://dashscope.aliyuncs.com/api/v1\",\n Website = \"https://tongyi.aliyun.com\",\n SupportedModels = new[] { \"qwen-turbo\", \"qwen-plus\", \"qwen-max (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"baidu\",\n DisplayName = \"Baidu ERNIE (coming soon)\",\n Icon = \"🐻\",\n Description = \"Baidu's ERNIE series models optimized for Chinese language understanding. BaseURL example: https://aip.baidubce.com/rpc/2.0/ai_custom/v1\",\n Website = \"https://cloud.baidu.com/product/wenxinworkshop\",\n SupportedModels = new[] { \"ernie-bot\", \"ernie-bot-turbo\", \"ernie-bot-4 (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"moonshot\",\n DisplayName = \"Moonshot AI (coming soon)\",\n Icon = \"🌙\",\n Description = \"Kimi models with exceptional long-context capabilities up to 2M tokens. BaseURL example: https://api.moonshot.cn/v1\",\n Website = \"https://kimi.moonshot.cn\",\n SupportedModels = new[] { \"moonshot-v1-8k\", \"moonshot-v1-32k\", \"moonshot-v1-128k (coming soon)\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n}"}, "9224dcfc9e5f5a4909bf4289ae2d01f225e27319": {"t": 1785760655.953439, "n": 15486, "h": "decb497a7e5026bc", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n\n public AIConfigController(IAIModelConfigService configService, ILogger<AIConfigController> logger)\n {\n _configService = configService;\n _logger = logger;\n }\n\n /// <summary>\n /// 获取当前租户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前租户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n // 服务层通过UserContext自动获取租户信息,不再依赖用户ID\n var userId = GetCurrentUserId(); // 保留用于兼容性,但服务层实际使用租户隔离\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get tenant default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理租户信息\n // 服务层会从UserContext自动获取TenantId和AppCode\n config.UserId = GetCurrentUserId(); // 保留用于审计追踪\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n config.UserId = GetCurrentUserId();\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n \n // 如果有配置ID,获取更新后的配置状态返回给前端\n if (config.Id > 0)\n {\n var updatedConfig = await _configService.GetConfigByIdAsync(config.Id);\n if (updatedConfig != null)\n {\n _logger.LogInformation(\"After test, config ID: {ConfigId} isAvailable: {IsAvailable}\", \n config.Id, updatedConfig.IsAvailable);\n }\n }\n \n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// Get supported AI providers list\n /// </summary>\n /// <returns>AI providers list</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"Industry-leading large language models from OpenAI, including GPT-4 and GPT-3.5. BaseURL example: https://api.openai.com/v1\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4\", \"gpt-4-turbo\", \"gpt-4o\", \"gpt-4o-mini\", \"gpt-3.5-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"claude\",\n DisplayName = \"Claude (Anthropic)\",\n Icon = \"🎭\",\n Description = \"Advanced AI models by Anthropic, focusing on safety and helpfulness. BaseURL example: https://api.anthropic.com\",\n Website = \"https://claude.ai\",\n SupportedModels = new[] { \"claude-3-opus-20240229\", \"claude-3-sonnet-20240229\", \"claude-3-haiku-20240307\", \"claude-3-5-sonnet-20241022\" }\n },\n new AIProviderInfo\n {\n Name = \"zhipuai\",\n DisplayName = \"ZhipuAI (GLM)\",\n Icon = \"🤖\",\n Description = \"Chinese AI models from ZhipuAI, supporting text generation and conversation. BaseURL example: https://open.bigmodel.cn/api/paas/v4\",\n Website = \"https://zhipuai.cn\",\n SupportedModels = new[] { \"glm-4\", \"glm-4v\", \"glm-4-plus\", \"glm-3-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"Specialized in code generation and mathematical reasoning. BaseURL example: https://api.deepseek.com/v1\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-coder\", \"deepseek-math\" }\n },\n new AIProviderInfo\n {\n Name = \"gemini\",\n DisplayName = \"Google Gemini (coming soon)\",\n Icon = \"💎\",\n Description = \"Google's multimodal AI models with advanced reasoning capabilities. BaseURL example: https://generativelanguage.googleapis.com/v1beta\",\n Website = \"https://ai.google.dev\",\n SupportedModels = new[] { \"gemini-pro\", \"gemini-pro-vision\", \"gemini-ultra (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"mistral\",\n DisplayName = \"Mistral AI (coming soon)\",\n Icon = \"🌪️\",\n Description = \"European AI company providing efficient and powerful language models. BaseURL example: https://api.mistral.ai/v1\",\n Website = \"https://mistral.ai\",\n SupportedModels = new[] { \"mistral-large\", \"mistral-medium\", \"mistral-small (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"cohere\",\n DisplayName = \"Cohere (coming soon)\",\n Icon = \"🧠\",\n Description = \"Enterprise-focused language models with strong multilingual capabilities. BaseURL example: https://api.cohere.ai/v1\",\n Website = \"https://cohere.com\",\n SupportedModels = new[] { \"command\", \"command-light\", \"command-nightly (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"qwen\",\n DisplayName = \"Alibaba Qwen (coming soon)\",\n Icon = \"☁️\",\n Description = \"Alibaba's Qwen series models with strong Chinese and English capabilities. BaseURL example: https://dashscope.aliyuncs.com/api/v1\",\n Website = \"https://tongyi.aliyun.com\",\n SupportedModels = new[] { \"qwen-turbo\", \"qwen-plus\", \"qwen-max (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"baidu\",\n DisplayName = \"Baidu ERNIE (coming soon)\",\n Icon = \"🐻\",\n Description = \"Baidu's ERNIE series models optimized for Chinese language understanding. BaseURL example: https://aip.baidubce.com/rpc/2.0/ai_custom/v1\",\n Website = \"https://cloud.baidu.com/product/wenxinworkshop\",\n SupportedModels = new[] { \"ernie-bot\", \"ernie-bot-turbo\", \"ernie-bot-4 (coming soon)\" }\n },\n new AIProviderInfo\n {\n Name = \"moonshot\",\n DisplayName = \"Moonshot AI (coming soon)\",\n Icon = \"🌙\",\n Description = \"Kimi models with exceptional long-context capabilities up to 2M tokens. BaseURL example: https://api.moonshot.cn/v1\",\n Website = \"https://kimi.moonshot.cn\",\n SupportedModels = new[] { \"moonshot-v1-8k\", \"moonshot-v1-32k\", \"moonshot-v1-128k (coming soon)\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n} "}, "82d2acf2d88daf1636073720b4478f2765f7d5fb": {"t": 1785760657.6235678, "n": 11030, "h": "6be608581b2585b6", "c": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing System.ComponentModel.DataAnnotations;\nusing FlowFlex.Application.Contracts.IServices;\nusing Item.Internal.StandardApi.Response;\nusing System.Net;\nusing FlowFlex.Domain.Entities.OW;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace FlowFlex.WebApi.Controllers.AI\n{\n /// <summary>\n /// AI Model Configuration API\n /// 提供AI模型配置管理功能\n /// </summary>\n [ApiController]\n [Route(\"ai/config/v{version:apiVersion}\")]\n [Display(Name = \"AI Model Configuration Service\")]\n [Tags(\"AI\", \"Configuration\", \"Model Management\")]\n [Authorize]\n public class AIConfigController : Controllers.ControllerBase\n {\n private readonly IAIModelConfigService _configService;\n private readonly ILogger<AIConfigController> _logger;\n\n public AIConfigController(IAIModelConfigService configService, ILogger<AIConfigController> logger)\n {\n _configService = configService;\n _logger = logger;\n }\n\n /// <summary>\n /// 获取当前用户的所有AI模型配置\n /// </summary>\n /// <returns>AI模型配置列表</returns>\n [HttpGet(\"models\")]\n [ProducesResponseType<SuccessResponse<List<AIModelConfig>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetUserAIModels()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的配置\n var userId = GetCurrentUserId();\n var configs = await _configService.GetUserAIModelConfigsAsync(userId);\n return Success(configs);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get user AI model configurations\");\n return BadRequest(\"Failed to get AI model configurations\");\n }\n }\n\n /// <summary>\n /// 获取当前用户的默认AI模型配置\n /// </summary>\n /// <returns>默认AI模型配置</returns>\n [HttpGet(\"models/default\")]\n [ProducesResponseType<SuccessResponse<AIModelConfig>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> GetDefaultAIModel()\n {\n try\n {\n // 使用租户隔离查询,只返回当前租户的默认配置\n var userId = GetCurrentUserId();\n var config = await _configService.GetUserDefaultConfigAsync(userId);\n if (config == null)\n {\n return BadRequest(\"Default AI model configuration not found\");\n }\n return Success(config);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get user default AI model configuration\");\n return BadRequest(\"Failed to get default AI model configuration\");\n }\n }\n\n /// <summary>\n /// 创建AI模型配置\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>创建结果</returns>\n [HttpPost(\"models\")]\n [ProducesResponseType<SuccessResponse<long>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> CreateAIModel([FromBody] AIModelConfig config)\n {\n try\n {\n // 使用租户隔离,UserContext会自动处理用户ID和租户信息\n config.UserId = GetCurrentUserId();\n var id = await _configService.CreateConfigAsync(config);\n return Success(id);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to create AI model configuration\");\n return BadRequest($\"Failed to create AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 更新AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>更新结果</returns>\n [HttpPut(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> UpdateAIModel(long id, [FromBody] AIModelConfig config)\n {\n try\n {\n config.Id = id;\n // 使用租户隔离,UserContext会自动处理租户信息\n config.UserId = GetCurrentUserId();\n var result = await _configService.UpdateConfigAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to update AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to update AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 删除AI模型配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>删除结果</returns>\n [HttpDelete(\"models/{id}\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> DeleteAIModel(long id)\n {\n try\n {\n // 使用租户隔离,不需要手动获取用户ID\n var result = await _configService.DeleteConfigAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to delete AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to delete AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 设置为默认配置\n /// </summary>\n /// <param name=\"id\">配置ID</param>\n /// <returns>设置结果</returns>\n [HttpPut(\"models/{id}/default\")]\n [ProducesResponseType<SuccessResponse<bool>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> SetAsDefault(long id)\n {\n try\n {\n var result = await _configService.SetAsDefaultAsync(id);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to set default AI model configuration, Config ID: {ConfigId}\", id);\n return BadRequest($\"Failed to set default AI model configuration: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 测试AI模型配置连接\n /// </summary>\n /// <param name=\"config\">AI模型配置</param>\n /// <returns>测试结果</returns>\n [HttpPost(\"models/test\")]\n [ProducesResponseType<SuccessResponse<AIModelTestResult>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public async Task<IActionResult> TestAIModelConnection([FromBody] AIModelConfig config)\n {\n try\n {\n var result = await _configService.TestConnectionAsync(config);\n return Success(result);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to test AI model configuration connection\");\n return BadRequest($\"Failed to test AI model configuration connection: {ex.Message}\");\n }\n }\n\n /// <summary>\n /// 获取支持的AI提供商列表\n /// </summary>\n /// <returns>AI提供商列表</returns>\n [HttpGet(\"providers\")]\n [ProducesResponseType<SuccessResponse<List<AIProviderInfo>>>((int)HttpStatusCode.OK)]\n [ProducesResponseType(typeof(ErrorResponse), 400)]\n public IActionResult GetProviders()\n {\n try\n {\n var providers = new List<AIProviderInfo>\n {\n new AIProviderInfo\n {\n Name = \"zhipuai\",\n DisplayName = \"智谱AI\",\n Icon = \"🤖\",\n Description = \"智谱AI GLM系列大模型,支持文本生成、对话问答等功能。BaseURL示例:https://open.bigmodel.cn/api/paas/v4\",\n Website = \"https://zhipuai.cn\",\n SupportedModels = new[] { \"glm-4\", \"glm-4v\", \"glm-3-turbo\" }\n },\n new AIProviderInfo\n {\n Name = \"openai\",\n DisplayName = \"OpenAI\",\n Icon = \"🚀\",\n Description = \"OpenAI GPT系列模型,业界领先的大语言模型。BaseURL示例:https://api.openai.com\",\n Website = \"https://openai.com\",\n SupportedModels = new[] { \"gpt-4\", \"gpt-4-turbo\", \"gpt-3.5-turbo\", \"gpt-4o\", \"gpt-4o-mini\" }\n },\n new AIProviderInfo\n {\n Name = \"claude\",\n DisplayName = \"Claude\",\n Icon = \"🎭\",\n Description = \"Anthropic Claude系列模型,注重安全和有用性。BaseURL示例:https://api.anthropic.com\",\n Website = \"https://claude.ai\",\n SupportedModels = new[] { \"claude-3-opus-20240229\", \"claude-3-sonnet-20240229\", \"claude-3-haiku-20240307\" }\n },\n new AIProviderInfo\n {\n Name = \"deepseek\",\n DisplayName = \"DeepSeek\",\n Icon = \"🔍\",\n Description = \"DeepSeek系列模型,专注于代码生成和数学推理。BaseURL示例:https://api.deepseek.com\",\n Website = \"https://deepseek.com\",\n SupportedModels = new[] { \"deepseek-chat\", \"deepseek-coder\", \"deepseek-math\" }\n }\n };\n\n return Success(providers);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"Failed to get AI providers list\");\n return BadRequest(\"Failed to get AI providers list\");\n }\n }\n }\n\n /// <summary>\n /// AI提供商信息\n /// </summary>\n public class AIProviderInfo\n {\n /// <summary>\n /// 提供商名称\n /// </summary>\n public string Name { get; set; } = string.Empty;\n\n /// <summary>\n /// 显示名称\n /// </summary>\n public string DisplayName { get; set; } = string.Empty;\n\n /// <summary>\n /// 图标\n /// </summary>\n public string Icon { get; set; } = string.Empty;\n\n /// <summary>\n /// 描述\n /// </summary>\n public string Description { get; set; } = string.Empty;\n\n /// <summary>\n /// 官网地址\n /// </summary>\n public string Website { get; set; } = string.Empty;\n\n /// <summary>\n /// 支持的模型列表\n /// </summary>\n public string[] SupportedModels { get; set; } = Array.Empty<string>();\n }\n} "}}}} |