|
@@ -0,0 +1,636 @@
|
|
|
+using System.Net.Http;
|
|
|
+using System.Net.Http.Headers;
|
|
|
+using System.Text.Json;
|
|
|
+using System.Text.Json.Serialization;
|
|
|
+using JsonSerializer = System.Text.Json.JsonSerializer;
|
|
|
+using System.IO;
|
|
|
+
|
|
|
+namespace OASystem.API.OAMethodLib.DeepSeekAPI
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// DeepSeek API 服务实现
|
|
|
+ /// </summary>
|
|
|
+ public class DeepSeekService : IDeepSeekService
|
|
|
+ {
|
|
|
+ private readonly HttpClient _httpClient;
|
|
|
+ private readonly ILogger<DeepSeekService> _logger;
|
|
|
+ private readonly IHostEnvironment _hostEnvironment;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 配置文件
|
|
|
+ /// </summary>
|
|
|
+ private DeepSeek DeepSeek { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 构造函数
|
|
|
+ /// </summary>
|
|
|
+ public DeepSeekService(IHostEnvironment hostEnvironment, ILogger<DeepSeekService> logger, HttpClient httpClient)
|
|
|
+ {
|
|
|
+ _hostEnvironment = hostEnvironment;
|
|
|
+ _httpClient = httpClient;
|
|
|
+ _logger = logger;
|
|
|
+ DeepSeek = AutofacIocManager.Instance.GetService<DeepSeek>();
|
|
|
+
|
|
|
+ // 设置基础地址和认证头
|
|
|
+ _httpClient.BaseAddress = new Uri(DeepSeek.BaseAddress ?? "https://api.deepseek.com/v1/");
|
|
|
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", DeepSeek.ApiKey);
|
|
|
+ _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 测试API连通性
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task<bool> TestApiConnectivityAsync()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var response = await _httpClient.GetAsync("models");
|
|
|
+ return response.IsSuccessStatusCode;
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查可用端点
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task<List<string>> DiscoverAvailableEndpointsAsync()
|
|
|
+ {
|
|
|
+ var endpoints = new List<string>
|
|
|
+ {
|
|
|
+ "models",
|
|
|
+ "chat/completions",
|
|
|
+ "files",
|
|
|
+ "uploads",
|
|
|
+ "documents",
|
|
|
+ "assistants"
|
|
|
+ };
|
|
|
+
|
|
|
+ var availableEndpoints = new List<string>();
|
|
|
+
|
|
|
+ foreach (var endpoint in endpoints)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var response = await _httpClient.GetAsync(endpoint);
|
|
|
+ if (response.IsSuccessStatusCode)
|
|
|
+ {
|
|
|
+ availableEndpoints.Add(endpoint);
|
|
|
+ Console.WriteLine($"✅ 端点可用: {endpoint}");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Console.WriteLine($"❌ 端点不可用: {endpoint} - {response.StatusCode}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"⚠️ 端点检查错误: {endpoint} - {ex.Message}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return availableEndpoints;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 上传文件到DeepSeek API
|
|
|
+ /// </summary>
|
|
|
+ public async Task<DeepSeekFileUploadResponse> UploadFileAsync(IFormFile file, string purpose = "assistants")
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _logger.LogInformation("开始上传文件: {FileName}, 大小: {Size} bytes", file.FileName, file.Length);
|
|
|
+
|
|
|
+ // 检查文件大小
|
|
|
+ if (file.Length > 512 * 1024 * 1024) // 512MB限制
|
|
|
+ {
|
|
|
+ throw new Exception($"文件大小超过限制: {file.Length} bytes");
|
|
|
+ }
|
|
|
+
|
|
|
+ using var content = new MultipartFormDataContent();
|
|
|
+ using var fileStream = file.OpenReadStream();
|
|
|
+ var fileContent = new StreamContent(fileStream);
|
|
|
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue(GetContentType(file.FileName));
|
|
|
+
|
|
|
+ content.Add(fileContent, "file", file.FileName);
|
|
|
+ content.Add(new StringContent(purpose), "purpose");
|
|
|
+
|
|
|
+ var response = await _httpClient.PostAsync("files", content);
|
|
|
+ _logger.LogInformation("文件上传路径:{filePath}", response.RequestMessage.RequestUri);
|
|
|
+ response.EnsureSuccessStatusCode();
|
|
|
+
|
|
|
+ var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
+ var result = JsonSerializer.Deserialize<DeepSeekFileUploadResponse>(responseContent, new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNameCaseInsensitive = true
|
|
|
+ });
|
|
|
+
|
|
|
+ _logger.LogInformation("文件上传成功: {FileName}, FileId: {FileId}", file.FileName, result.Id);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "文件上传失败: {FileName}", file.FileName);
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 批量上传文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<List<FileUploadResult>> UploadFilesAsync(List<IFormFile> files, string purpose = "assistants")
|
|
|
+ {
|
|
|
+ var results = new List<FileUploadResult>();
|
|
|
+
|
|
|
+ foreach (var file in files)
|
|
|
+ {
|
|
|
+ var result = new FileUploadResult
|
|
|
+ {
|
|
|
+ FileName = file.FileName,
|
|
|
+ FileSize = file.Length
|
|
|
+ };
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var uploadResponse = await UploadFileAsync(file, purpose);
|
|
|
+ result.FileId = uploadResponse.Id;
|
|
|
+ result.Success = true;
|
|
|
+ result.Status = uploadResponse.Status;
|
|
|
+ result.Message = "上传成功";
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ result.Success = false;
|
|
|
+ result.Message = $"上传失败: {ex.Message}";
|
|
|
+ result.Status = "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ results.Add(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取文件列表
|
|
|
+ /// </summary>
|
|
|
+ public async Task<DeepSeekFileListResponse> ListFilesAsync()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var response = await _httpClient.GetAsync("files");
|
|
|
+ response.EnsureSuccessStatusCode();
|
|
|
+
|
|
|
+ var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
+ return JsonSerializer.Deserialize<DeepSeekFileListResponse>(responseContent, new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNameCaseInsensitive = true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "获取文件列表失败");
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取文件信息
|
|
|
+ /// </summary>
|
|
|
+ public async Task<DeepSeekFileUploadResponse> GetFileInfoAsync(string fileId)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var response = await _httpClient.GetAsync($"files/{fileId}");
|
|
|
+ response.EnsureSuccessStatusCode();
|
|
|
+
|
|
|
+ var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
+ return JsonSerializer.Deserialize<DeepSeekFileUploadResponse>(responseContent, new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNameCaseInsensitive = true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "获取文件信息失败: {FileId}", fileId);
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 删除文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<bool> DeleteFileAsync(string fileId)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var response = await _httpClient.DeleteAsync($"files/{fileId}");
|
|
|
+ response.EnsureSuccessStatusCode();
|
|
|
+
|
|
|
+ _logger.LogInformation("文件删除成功: {FileId}", fileId);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "文件删除失败: {FileId}", fileId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 使用已上传的文件进行聊天
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ApiResponse> ChatWithFilesAsync(List<string> fileIds, string question, string model = "deepseek-chat", float temperature = 0.7f, int maxTokens = 4000)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 等待所有文件处理完成
|
|
|
+ var processedFiles = new List<DeepSeekFileUploadResponse>();
|
|
|
+ foreach (var fileId in fileIds)
|
|
|
+ {
|
|
|
+ var fileInfo = await WaitForFileProcessingAsync(fileId);
|
|
|
+ processedFiles.Add(fileInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建消息内容
|
|
|
+ var messageContent = new List<object>
|
|
|
+ {
|
|
|
+ new { type = "text", text = question }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加文件引用
|
|
|
+ foreach (var file in processedFiles)
|
|
|
+ {
|
|
|
+ messageContent.Add(new
|
|
|
+ {
|
|
|
+ type = "file",
|
|
|
+ file_id = file.Id
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ var request = new DeepSeekChatWithFilesRequest
|
|
|
+ {
|
|
|
+ Model = model,
|
|
|
+ Messages = new List<FileMessage>
|
|
|
+ {
|
|
|
+ new FileMessage
|
|
|
+ {
|
|
|
+ Role = "user",
|
|
|
+ Content = messageContent
|
|
|
+ }
|
|
|
+ },
|
|
|
+ Temperature = temperature,
|
|
|
+ MaxTokens = maxTokens
|
|
|
+ };
|
|
|
+
|
|
|
+ var jsonContent = JsonSerializer.Serialize(request, new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
|
+ });
|
|
|
+
|
|
|
+ var httpContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
|
|
+ var response = await _httpClient.PostAsync("chat/completions", httpContent);
|
|
|
+ response.EnsureSuccessStatusCode();
|
|
|
+
|
|
|
+ var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
+ var chatResponse = JsonSerializer.Deserialize<DeepSeekResponse>(responseContent, new JsonSerializerOptions
|
|
|
+ {
|
|
|
+ PropertyNameCaseInsensitive = true
|
|
|
+ });
|
|
|
+
|
|
|
+ return new ApiResponse
|
|
|
+ {
|
|
|
+ Success = true,
|
|
|
+ Message = "聊天成功",
|
|
|
+ Answer = chatResponse.Choices[0].Message.Content,
|
|
|
+ TokensUsed = chatResponse.Usage.TotalTokens
|
|
|
+ };
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "使用文件聊天失败");
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 等待文件处理完成
|
|
|
+ /// </summary>
|
|
|
+ public async Task<DeepSeekFileUploadResponse> WaitForFileProcessingAsync(string fileId, int timeoutSeconds = 60)
|
|
|
+ {
|
|
|
+ var startTime = DateTime.UtcNow;
|
|
|
+ var timeout = TimeSpan.FromSeconds(timeoutSeconds);
|
|
|
+
|
|
|
+ while (DateTime.UtcNow - startTime < timeout)
|
|
|
+ {
|
|
|
+ var fileInfo = await GetFileInfoAsync(fileId);
|
|
|
+
|
|
|
+ if (fileInfo.Status == "processed")
|
|
|
+ {
|
|
|
+ _logger.LogInformation("文件处理完成: {FileId}", fileId);
|
|
|
+ return fileInfo;
|
|
|
+ }
|
|
|
+ else if (fileInfo.Status == "error")
|
|
|
+ {
|
|
|
+ throw new Exception($"文件处理失败: {fileId}");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 等待2秒后重试
|
|
|
+ await Task.Delay(2000);
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new TimeoutException($"文件处理超时: {fileId}");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 根据文件名获取Content-Type
|
|
|
+ /// </summary>
|
|
|
+ private static string GetContentType(string fileName)
|
|
|
+ {
|
|
|
+ var extension = Path.GetExtension(fileName).ToLower();
|
|
|
+ return extension switch
|
|
|
+ {
|
|
|
+ ".txt" => "text/plain",
|
|
|
+ ".pdf" => "application/pdf",
|
|
|
+ ".json" => "application/json",
|
|
|
+ ".csv" => "text/csv",
|
|
|
+ ".html" => "text/html",
|
|
|
+ ".htm" => "text/html",
|
|
|
+ ".md" => "text/markdown",
|
|
|
+ _ => "application/octet-stream"
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 项目相关
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取项目内的指定文件并转换为IFormFile
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadProjectFilesAsync(List<string> relativePaths)
|
|
|
+ {
|
|
|
+ var response = new ProjectFileReadResponse();
|
|
|
+ var projectRoot = GetProjectRootPath();
|
|
|
+ if (projectRoot.Contains("OASystem.Api"))
|
|
|
+ {
|
|
|
+ projectRoot = projectRoot.Replace("OASystem.Api", "");
|
|
|
+ }
|
|
|
+
|
|
|
+ _logger.LogInformation("开始读取项目文件,数量: {Count}", relativePaths.Count);
|
|
|
+
|
|
|
+ foreach (var relativePath in relativePaths)
|
|
|
+ {
|
|
|
+ var fileInfo = new ProjectFileInfo
|
|
|
+ {
|
|
|
+ RelativePath = relativePath
|
|
|
+ };
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var fullPath = Path.Combine(projectRoot, relativePath);
|
|
|
+ fileInfo.FullPath = fullPath;
|
|
|
+
|
|
|
+ if (!System.IO.File.Exists(fullPath))
|
|
|
+ {
|
|
|
+ throw new FileNotFoundException($"文件不存在: {relativePath}");
|
|
|
+ }
|
|
|
+
|
|
|
+ var fileInfoObj = new FileInfo(fullPath);
|
|
|
+ fileInfo.FileSize = fileInfoObj.Length;
|
|
|
+ fileInfo.LastModified = fileInfoObj.LastWriteTime;
|
|
|
+
|
|
|
+ // 转换为IFormFile
|
|
|
+ fileInfo.FormFile = await ConvertToFormFileAsync(fullPath);
|
|
|
+ fileInfo.Success = true;
|
|
|
+
|
|
|
+ _logger.LogInformation("文件读取成功: {FileName}, 大小: {Size} bytes", relativePath, fileInfo.FileSize);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ fileInfo.Success = false;
|
|
|
+ fileInfo.ErrorMessage = ex.Message;
|
|
|
+ _logger.LogError(ex, "文件读取失败: {FileName}", relativePath);
|
|
|
+ }
|
|
|
+
|
|
|
+ response.FileInfos.Add(fileInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ response.Success = response.SuccessCount > 0;
|
|
|
+ response.Message = $"成功读取 {response.SuccessCount} 个文件,失败 {response.FailureCount} 个";
|
|
|
+
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取项目内指定目录的文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadProjectDirectoryAsync(string directoryPath, string searchPattern = "*.cs", bool recursive = false)
|
|
|
+ {
|
|
|
+ var response = new ProjectFileReadResponse();
|
|
|
+ var projectRoot = GetProjectRootPath();
|
|
|
+ var fullDirectoryPath = Path.Combine(projectRoot, directoryPath);
|
|
|
+
|
|
|
+ _logger.LogInformation("开始读取目录文件: {Directory}, 模式: {Pattern}", directoryPath, searchPattern);
|
|
|
+
|
|
|
+ if (!Directory.Exists(fullDirectoryPath))
|
|
|
+ {
|
|
|
+ response.Success = false;
|
|
|
+ response.Message = $"目录不存在: {directoryPath}";
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
|
|
+ var files = Directory.GetFiles(fullDirectoryPath, searchPattern, searchOption);
|
|
|
+
|
|
|
+ _logger.LogInformation("找到 {FileCount} 个文件", files.Length);
|
|
|
+
|
|
|
+ foreach (var filePath in files)
|
|
|
+ {
|
|
|
+ var relativePath = Path.GetRelativePath(projectRoot, filePath);
|
|
|
+ var fileInfo = new ProjectFileInfo
|
|
|
+ {
|
|
|
+ RelativePath = relativePath,
|
|
|
+ FullPath = filePath
|
|
|
+ };
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var fileInfoObj = new FileInfo(filePath);
|
|
|
+ fileInfo.FileSize = fileInfoObj.Length;
|
|
|
+ fileInfo.LastModified = fileInfoObj.LastWriteTime;
|
|
|
+
|
|
|
+ // 转换为IFormFile
|
|
|
+ fileInfo.FormFile = await ConvertToFormFileAsync(filePath);
|
|
|
+ fileInfo.Success = true;
|
|
|
+
|
|
|
+ _logger.LogDebug("文件读取成功: {FileName}", relativePath);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ fileInfo.Success = false;
|
|
|
+ fileInfo.ErrorMessage = ex.Message;
|
|
|
+ _logger.LogError(ex, "文件读取失败: {FileName}", relativePath);
|
|
|
+ }
|
|
|
+
|
|
|
+ response.FileInfos.Add(fileInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ response.Success = response.SuccessCount > 0;
|
|
|
+ response.Message = $"成功读取 {response.SuccessCount} 个文件,失败 {response.FailureCount} 个";
|
|
|
+
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取项目根目录路径
|
|
|
+ /// </summary>
|
|
|
+ public string GetProjectRootPath()
|
|
|
+ {
|
|
|
+ // 在开发环境中使用ContentRootPath,在生产环境中可能需要调整
|
|
|
+ return _hostEnvironment.ContentRootPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查文件是否存在
|
|
|
+ /// </summary>
|
|
|
+ public bool FileExists(string relativePath)
|
|
|
+ {
|
|
|
+ var fullPath = Path.Combine(GetProjectRootPath(), relativePath);
|
|
|
+ return System.IO.File.Exists(fullPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 将物理文件转换为IFormFile
|
|
|
+ /// </summary>
|
|
|
+ public async Task<IFormFile> ConvertToFormFileAsync(string filePath)
|
|
|
+ {
|
|
|
+ var fileInfo = new FileInfo(filePath);
|
|
|
+ var fileName = Path.GetFileName(filePath);
|
|
|
+
|
|
|
+ // 读取文件内容
|
|
|
+ byte[] fileBytes;
|
|
|
+ using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
|
|
+ {
|
|
|
+ fileBytes = new byte[fileStream.Length];
|
|
|
+ await fileStream.ReadAsync(fileBytes, 0, (int)fileStream.Length);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建IFormFile
|
|
|
+ return new FormFile(
|
|
|
+ new MemoryStream(fileBytes),
|
|
|
+ 0,
|
|
|
+ fileBytes.Length,
|
|
|
+ "file",
|
|
|
+ fileName)
|
|
|
+ {
|
|
|
+ Headers = new HeaderDictionary(),
|
|
|
+ ContentType = GetFileContentType(fileName)
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 根据文件名获取Content-Type
|
|
|
+ /// </summary>
|
|
|
+ private string GetFileContentType(string fileName)
|
|
|
+ {
|
|
|
+ var extension = Path.GetExtension(fileName).ToLower();
|
|
|
+ return extension switch
|
|
|
+ {
|
|
|
+ ".cs" => "text/plain",
|
|
|
+ ".txt" => "text/plain",
|
|
|
+ ".json" => "application/json",
|
|
|
+ ".xml" => "application/xml",
|
|
|
+ ".html" => "text/html",
|
|
|
+ ".htm" => "text/html",
|
|
|
+ ".css" => "text/css",
|
|
|
+ ".js" => "application/javascript",
|
|
|
+ ".ts" => "application/typescript",
|
|
|
+ ".py" => "text/x-python",
|
|
|
+ ".java" => "text/x-java",
|
|
|
+ ".cpp" => "text/x-c++",
|
|
|
+ ".c" => "text/x-c",
|
|
|
+ ".h" => "text/x-c",
|
|
|
+ ".md" => "text/markdown",
|
|
|
+ _ => "application/octet-stream"
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取特定的签证申请表单文件
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="fileName"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadVisaFormFileAsync(string fileName)
|
|
|
+ {
|
|
|
+ var specificPath = $@"OASystem\OASystem.Domain\ViewModels\VisaFormDetails\{fileName}";
|
|
|
+ return await ReadProjectFilesAsync(new List<string> { specificPath });
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取OASystem项目中的所有CS文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadOASystemFilesAsync(string searchPattern = "*.cs", bool recursive = true)
|
|
|
+ {
|
|
|
+ return await ReadProjectDirectoryAsync(@"OASystem", searchPattern, recursive);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取指定域模型中的CS文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadDomainViewModelsAsync(string searchPattern = "*.cs", bool recursive = true)
|
|
|
+ {
|
|
|
+ return await ReadProjectDirectoryAsync(@"OASystem\OASystem.Domain\ViewModels", searchPattern, recursive);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 读取签证表单相关的所有CS文件
|
|
|
+ /// </summary>
|
|
|
+ public async Task<ProjectFileReadResponse> ReadVisaFormFilesAsync(string searchPattern = "*.cs", bool recursive = true)
|
|
|
+ {
|
|
|
+ return await ReadProjectDirectoryAsync(@"OASystem\OASystem.Domain\ViewModels\VisaFormDetails", searchPattern, recursive);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 私有实体类
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// DeepSeek 聊天响应(用于反序列化)
|
|
|
+ /// </summary>
|
|
|
+ internal class DeepSeekResponse
|
|
|
+ {
|
|
|
+ [JsonPropertyName("choices")]
|
|
|
+ public List<Choice> Choices { get; set; }
|
|
|
+
|
|
|
+ [JsonPropertyName("usage")]
|
|
|
+ public Usage Usage { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class Choice
|
|
|
+ {
|
|
|
+ [JsonPropertyName("message")]
|
|
|
+ public Message Message { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class Message
|
|
|
+ {
|
|
|
+ [JsonPropertyName("content")]
|
|
|
+ public string Content { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class Usage
|
|
|
+ {
|
|
|
+ [JsonPropertyName("total_tokens")]
|
|
|
+ public int TotalTokens { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+}
|