Browse Source

ai 识别签字表单

Lyyyi 3 days ago
parent
commit
d2f145ff47

+ 197 - 61
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -10,6 +10,7 @@ using NPOI.XSSF.UserModel;
 using OASystem.API.Middlewares;
 using OASystem.API.OAMethodLib;
 using OASystem.API.OAMethodLib.APNs;
+using OASystem.API.OAMethodLib.DeepSeekAPI;
 using OASystem.API.OAMethodLib.File;
 using OASystem.API.OAMethodLib.Hub.HubClients;
 using OASystem.API.OAMethodLib.Hub.Hubs;
@@ -111,6 +112,8 @@ namespace OASystem.API.Controllers
         private readonly EnterExitCostQuoteRepository _enterExitCostQuoteRep;
         private readonly GroupOrderPreInfoRepository _grpOrderPreInfoRep;
 
+        private readonly IDeepSeekService _deepSeekService;
+
         /// <summary>
         /// 构造函数
         /// </summary>
@@ -198,7 +201,8 @@ namespace OASystem.API.Controllers
             EnterExitCostDraftRepository enterExitCostDraftRep,
             RestaurantRepository restaurantRep,
             EnterExitCostQuoteRepository enterExitCostQuoteRep,
-            GroupOrderPreInfoRepository grpOrderPreInfoRep
+            GroupOrderPreInfoRepository grpOrderPreInfoRep,
+            IDeepSeekService deepSeekService
             )
         {
             _logger = logger;
@@ -258,7 +262,7 @@ namespace OASystem.API.Controllers
             _restaurantRep = restaurantRep;
             _enterExitCostQuoteRep = enterExitCostQuoteRep;
             _grpOrderPreInfoRep = grpOrderPreInfoRep;
-
+            _deepSeekService = deepSeekService;
         }
 
         #region 流程管控
@@ -2746,6 +2750,33 @@ FROM
         #region 团组&签证
 
         #region 团组&签证 New
+
+        /// <summary>
+        ///  团组&签证 New
+        ///  DeepSeek API:测试API连通性
+        /// </summary>
+        /// <param name="file">请求dto</param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> TestDeepSeekApiConnectivityAsync()
+        {
+            return Ok(JsonView(await _deepSeekService.TestApiConnectivityAsync()));
+        }
+
+        /// <summary>
+        ///  团组&签证 New
+        ///  DeepSeek API:检查可用端点
+        /// </summary>
+        /// <param name="file">请求dto</param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> DiscoverAvailableEndpointsAsync()
+        {
+            return Ok(JsonView(await _deepSeekService.DiscoverAvailableEndpointsAsync()));
+        }
+
         /// <summary>
         ///  团组&签证 New
         ///  上传签证文件(客户填写签证文件),新增或更新 签证信息
@@ -2754,10 +2785,10 @@ FROM
         /// <returns></returns>
         [HttpPost]
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
-        public async Task<IActionResult> VisaFileUploadAndUpdate(IFormFile file, int fileTypeId)
+        public async Task<IActionResult> VisaFileUploadAndUpdate(IFormFile visaFile, int fileTypeId)
         {
             //文件验证
-            if (file.Length < 1) return Ok(JsonView(false, "请选择文件!"));
+            if (visaFile.Length < 1) return Ok(JsonView(false, "请选择文件!"));
 
             //var visaFiles = new VisaUploadFileTypeView();
             var filetypes = VisaUploadFileTypeView.GetVisaUploadFileTypeViewItemInit();
@@ -2766,87 +2797,184 @@ FROM
             //服务器存储文件
             var fileServerPath = $"D:/FTP/File/OA2023/Office/Word/VisaClientData/Uploads";
             if (!Directory.Exists(fileServerPath)) Directory.CreateDirectory(fileServerPath);
-            var filePath = Path.Combine(fileServerPath, file.FileName);
+            var filePath = Path.Combine(fileServerPath, visaFile.FileName);
             using (var stream = new FileStream(filePath, FileMode.Create))
             {
-                await file.CopyToAsync(stream);
+                await visaFile.CopyToAsync(stream);
             }
             //日志记录-上传文件
-            _logger.LogInformation("【签证上传文件操作信息】签证文件存储成功!路径:{fileServerPath} 文件名称:{fileName}", fileServerPath, file.FileName);
+            _logger.LogInformation("【签证上传文件操作信息】签证文件存储成功!路径:{fileServerPath} 文件名称:{fileName}", fileServerPath, visaFile.FileName);
 
-            //kimi AI 识别文档返回json结果
-            int kimiAI_API_Index = 0; //KIMI-AI-API 访问计次
-            var KiMiApi = new KiMiApiClient();
-            var files = new List<IFormFile>() { file };
-            var seedConter = await KiMiApi.UploadFilesAsync(files);
+            IFormFile localFile = null;
+            //获取项目中的cs文件
+            try
+            {
+                _logger.LogInformation("读取并上传项目文件请求");
 
-            if (seedConter.Count == 0) return Ok(JsonView(false, "Kimi AI 文件上传结果为空!"));
+                // 1. 读取项目文件
+                var readResponse = await _deepSeekService.ReadProjectFilesAsync(new List<string>() { @"OASystem.Domain\ViewModels\VisaFormDetails\AusNewVisaApplicationForm.cs" });
 
-            var seedMessage = filetypes.Where(x => x.FileId == fileTypeId).FirstOrDefault()?.KimiAITips;
-            var messages = new List<SeedMessages>
+                if (readResponse.SuccessCount == 0)
+                {
+                    return Ok(JsonView(false, "读取项目文件失败"));
+                }
+
+                localFile = readResponse.FileInfos.FirstOrDefault().FormFile;
+            }
+            catch (Exception ex)
             {
-                new()  { Role = KimiRole.system, Content = "你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长识别文件为Json格式的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。" },
-                new()  { Role = KimiRole.user, Content = seedMessage }
+                _logger.LogError(ex, "读取项目文件失败");
+                return Ok(JsonView(false, "读取项目文件失败"));
+            }
+
+            if (localFile == null)
+            {
+                return Ok(JsonView(false, "读取项目文件失败"));
+            }
+
+            var fileReq = new DeepSeekFileUploadRequest() {
+                Files = new List<IFormFile> { localFile, visaFile },
             };
-            messages.InsertRange(1, seedConter);
+            var filesResult = new List<FileUploadResult>();
 
-            string jsonString = string.Empty;
-            bool isJson = false; // 是否是json格式 不是json格式继续访问AI
-            while (!isJson)
+            #region 上传文件到DeepSeekAPI
+
+            try
             {
-                kimiAI_API_Index++;
-                var kimiApiResult = await KiMiApi.SeedMessage(messages);
-                var kimiApiResult_JObject = JObject.Parse(kimiApiResult);
-                jsonString += kimiApiResult_JObject["content"].ToString();
+                _logger.LogInformation("开始上传 {FileCount} 个文件到DeepSeek API", fileReq.Files.Count);
 
-                string startStr = "```json", endStr = "```";
+                filesResult = await _deepSeekService.UploadFilesAsync(fileReq.Files, fileReq.Purpose);
 
-                // 判断并清除开头部分
-                if (jsonString.StartsWith(startStr))
+                // 需要等待处理完成
+                if (fileReq.WaitForProcessing)
                 {
-                    jsonString = jsonString.Replace(startStr, "");
+                    foreach (var result in filesResult)
+                    {
+                        if (result.Success)
+                        {
+                            try
+                            {
+                                var processedFile = await _deepSeekService.WaitForFileProcessingAsync(result.FileId);
+                                result.Status = processedFile.Status;
+                            }
+                            catch (Exception ex)
+                            {
+                                _logger.LogError(ex, "DeepSeek API文件上传失败");
+                                return Ok(JsonView(false, $"DeepSeek API文件上传处理失败: {ex.Message}"));
+                            }
+                        }
+                    }
                 }
 
-                // 判断并清除结尾部分
-                if (jsonString.EndsWith(endStr))
-                {
-                    jsonString = jsonString.Replace(endStr, "");
-                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "DeepSeek API文件上传失败");
+                return Ok(JsonView(false, $"DeepSeek API文件上传处理失败: {ex.Message}"));
+            }
 
-                //验证json格式是否正确
-                try
-                {
-                    JsonDocument.Parse(jsonString);
-                    isJson = true; // 如果解析成功,则是json格式
-                }
-                catch (Exception)
-                {
-                    //messages = new List<SeedMessages>() { new() { Role = KimiRole.user, Content = "接着说" } };
-                    messages.Add(new() { Role = KimiRole.user, Content = "接着上一个话题继续完成未完成的内容。" });
-                }
+            #endregion
+
+            if (filesResult == null || filesResult.Count < 1)
+            {
+                return Ok(JsonView(false, $"DeepSeek API文件上传处理失败"));
             }
 
-            var clientInfo = new Crm_DeleClient();            //客户资料表
-            var clientFamily = new Crm_VisaCustomerFamily();  //客户家庭表
-            var clientCompany = new Crm_CustomerCompany();    //客户公司表
-            var clientCert = new Crm_CustomerCert();          //客户证件表
-            var clientSchool = new Crm_VisaCustomerSchool();  //客户学历表
+            //
+            try
+            {
+                var fileIds = filesResult.Where(x => x.Success).Select(x => x.FileId).ToList();
+                _logger.LogInformation("使用 {FileCount} 个文件进行聊天", fileIds.Count);
+                string question = string.Format("将上传的文件内容按照识别为JSON格式,并按照 AusNewVisaApplicationForm 指定的实体类结构进行映射。确保字段和值对应、文档中数据的完整性、复选框选择打勾后的文本。第一项从不识别\"比如美国(2024年7月29日)\"相关文字。");
+                var response = await _deepSeekService.ChatWithFilesAsync(fileIds,question);
 
-            //按照表格类型分别进行参数处理
-            switch (fileTypeId)
+                return Ok(response);
+            }
+            catch (Exception ex)
             {
-                //澳新签证个人申请表
-                case 1:
+                _logger.LogError(ex, "使用文件聊天失败");
+                return Ok(JsonView(false, $"聊天失败: {ex.Message}"));
+            }
 
-                    _logger.LogInformation("【签证上传文件操作信息】【澳新签证个人申请表】【KIMI-AI-API访问计次】{count} 次", kimiAI_API_Index);
-                    VisaApplication resInfo = System.Text.Json.JsonSerializer.Deserialize<VisaApplication>(jsonString);
 
-                    break;
-                default:
 
-                    break;
+            //加载文件OpCarTouristGuideGroundContent
 
-            }
+
+
+            ////kimi AI 识别文档返回json结果
+            //int kimiAI_API_Index = 0; //KIMI-AI-API 访问计次
+            //var KiMiApi = new KiMiApiClient();
+            //var files = new List<IFormFile>() { file };
+            //var seedConter = await KiMiApi.UploadFilesAsync(files);
+
+            //if (seedConter.Count == 0) return Ok(JsonView(false, "Kimi AI 文件上传结果为空!"));
+
+            //var seedMessage = filetypes.Where(x => x.FileId == fileTypeId).FirstOrDefault()?.KimiAITips;
+            //var messages = new List<SeedMessages>
+            //{
+            //    new()  { Role = KimiRole.system, Content = "你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长识别文件为Json格式的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。" },
+            //    new()  { Role = KimiRole.user, Content = seedMessage }
+            //};
+            //messages.InsertRange(1, seedConter);
+
+            string jsonString = string.Empty;
+            //bool isJson = false; // 是否是json格式 不是json格式继续访问AI
+            //while (!isJson)
+            //{
+            //    kimiAI_API_Index++;
+            //    var kimiApiResult = await KiMiApi.SeedMessage(messages);
+            //    var kimiApiResult_JObject = JObject.Parse(kimiApiResult);
+            //    jsonString += kimiApiResult_JObject["content"].ToString();
+
+            //    string startStr = "```json", endStr = "```";
+
+            //    // 判断并清除开头部分
+            //    if (jsonString.StartsWith(startStr))
+            //    {
+            //        jsonString = jsonString.Replace(startStr, "");
+            //    }
+
+            //    // 判断并清除结尾部分
+            //    if (jsonString.EndsWith(endStr))
+            //    {
+            //        jsonString = jsonString.Replace(endStr, "");
+            //    }
+
+            //    //验证json格式是否正确
+            //    try
+            //    {
+            //        JsonDocument.Parse(jsonString);
+            //        isJson = true; // 如果解析成功,则是json格式
+            //    }
+            //    catch (Exception)
+            //    {
+            //        //messages = new List<SeedMessages>() { new() { Role = KimiRole.user, Content = "接着说" } };
+            //        messages.Add(new() { Role = KimiRole.user, Content = "接着上一个话题继续完成未完成的内容。" });
+            //    }
+            //}
+
+            //var clientInfo = new Crm_DeleClient();            //客户资料表
+            //var clientFamily = new Crm_VisaCustomerFamily();  //客户家庭表
+            //var clientCompany = new Crm_CustomerCompany();    //客户公司表
+            //var clientCert = new Crm_CustomerCert();          //客户证件表
+            //var clientSchool = new Crm_VisaCustomerSchool();  //客户学历表
+
+            ////按照表格类型分别进行参数处理
+            //switch (fileTypeId)
+            //{
+            //    //澳新签证个人申请表
+            //    case 1:
+
+            //        _logger.LogInformation("【签证上传文件操作信息】【澳新签证个人申请表】【KIMI-AI-API访问计次】{count} 次", kimiAI_API_Index);
+            //        VisaApplication resInfo = System.Text.Json.JsonSerializer.Deserialize<VisaApplication>(jsonString);
+
+            //        break;
+            //    default:
+
+            //        break;
+
+            //}
 
 
 
@@ -2854,7 +2982,15 @@ FROM
         }
 
 
-
+        private int GetSuccessCount(List<FileUploadResult> results)
+        {
+            var count = 0;
+            foreach (var result in results)
+            {
+                if (result.Success) count++;
+            }
+            return count;
+        }
         #endregion
 
 

+ 9 - 0
OASystem/OASystem.Api/OAMethodLib/AutofacRegister.cs

@@ -1,4 +1,5 @@
 using Autofac;
+using OASystem.API.OAMethodLib.DeepSeekAPI;
 using OASystem.Domain.Dtos.Business;
 using OASystem.Domain.Dtos.Groups;
 using OASystem.Domain.Dtos.KiMi;
@@ -35,6 +36,14 @@ namespace OASystem.API.OAMethodLib
                  .As<KiMiSetting>()
                  .SingleInstance();
 
+            builder.RegisterInstance(AppSettingsHelper.GetSection("DeepSeek").Get<DeepSeek>() ?? new DeepSeek
+                {
+                    BaseAddress = AppSettingsHelper.Get("DeepSeek:BaseAddress"),
+                    ApiKey = AppSettingsHelper.Get("DeepSeek:ApiKey")
+                })
+                .As<DeepSeek>()
+                .SingleInstance();
+
             #region 团组结束通知短信
             DeleReminderConfig _deleReminderConfig = new DeleReminderConfig();
             _deleReminderConfig.Test = AppSettingsHelper.Get(DeleReminderConfig.KEY, "Test");

+ 395 - 0
OASystem/OASystem.Api/OAMethodLib/DeepSeekAPI/DeepSeekModels.cs

@@ -0,0 +1,395 @@
+using System.Text.Json.Serialization;
+
+namespace OASystem.API.OAMethodLib.DeepSeekAPI
+{
+    public class DeepSeekModels
+    {
+    }
+
+    /// <summary>
+    /// 基础信息
+    /// </summary>
+    public class DeepSeek
+    {
+        /// <summary>
+        /// ApiKey
+        /// </summary>
+        public string ApiKey { get; set; }
+
+        /// <summary>
+        /// BaseAddress
+        /// </summary>
+        public string BaseAddress { get; set; }
+    }
+
+    /// <summary>
+    /// 文件上传到DeepSeek API的请求参数
+    /// </summary>
+    public class DeepSeekFileUploadRequest
+    {
+        /// <summary>
+        /// 要上传的文件列表
+        /// </summary>
+        public List<IFormFile> Files { get; set; } = new List<IFormFile>();
+
+        /// <summary>
+        /// 用途描述(可选)
+        /// </summary>
+        public string Purpose { get; set; } = "assistants";
+
+        /// <summary>
+        /// 模型名称,默认为 deepseek-chat
+        /// </summary>
+        public string Model { get; set; } = "deepseek-chat";
+
+        /// <summary>
+        /// 是否等待文件处理完成
+        /// </summary>
+        public bool WaitForProcessing { get; set; } = true;
+    }
+
+    /// <summary>
+    /// 使用已上传文件提问的请求参数
+    /// </summary>
+    public class AskWithFilesRequest
+    {
+        /// <summary>
+        /// 已上传的文件ID列表
+        /// </summary>
+        public List<string> FileIds { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 问题描述
+        /// </summary>
+        public string Question { get; set; }
+
+        /// <summary>
+        /// 模型名称
+        /// </summary>
+        public string Model { get; set; } = "deepseek-chat";
+
+        /// <summary>
+        /// 温度参数
+        /// </summary>
+        public float Temperature { get; set; } = 0.7f;
+
+        /// <summary>
+        /// 最大token数
+        /// </summary>
+        public int MaxTokens { get; set; } = 4000;
+    }
+
+    /// <summary>
+    /// DeepSeek 文件上传响应
+    /// </summary>
+    public class DeepSeekFileUploadResponse
+    {
+        /// <summary>
+        /// 文件ID
+        /// </summary>
+        [JsonPropertyName("id")]
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 对象类型
+        /// </summary>
+        [JsonPropertyName("object")]
+        public string Object { get; set; }
+
+        /// <summary>
+        /// 文件大小(字节)
+        /// </summary>
+        [JsonPropertyName("bytes")]
+        public long Bytes { get; set; }
+
+        /// <summary>
+        /// 创建时间戳
+        /// </summary>
+        [JsonPropertyName("created_at")]
+        public long CreatedAt { get; set; }
+
+        /// <summary>
+        /// 文件名
+        /// </summary>
+        [JsonPropertyName("filename")]
+        public string Filename { get; set; }
+
+        /// <summary>
+        /// 文件用途
+        /// </summary>
+        [JsonPropertyName("purpose")]
+        public string Purpose { get; set; }
+
+        /// <summary>
+        /// 文件状态
+        /// </summary>
+        [JsonPropertyName("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 状态详情
+        /// </summary>
+        [JsonPropertyName("status_details")]
+        public object StatusDetails { get; set; }
+    }
+
+    /// <summary>
+    /// 文件列表响应
+    /// </summary>
+    public class DeepSeekFileListResponse
+    {
+        /// <summary>
+        /// 对象类型
+        /// </summary>
+        [JsonPropertyName("object")]
+        public string Object { get; set; }
+
+        /// <summary>
+        /// 文件数据列表
+        /// </summary>
+        [JsonPropertyName("data")]
+        public List<DeepSeekFileUploadResponse> Data { get; set; }
+    }
+
+    /// <summary>
+    /// 使用文件进行聊天的请求
+    /// </summary>
+    public class DeepSeekChatWithFilesRequest
+    {
+        /// <summary>
+        /// 模型名称
+        /// </summary>
+        [JsonPropertyName("model")]
+        public string Model { get; set; }
+
+        /// <summary>
+        /// 消息列表
+        /// </summary>
+        [JsonPropertyName("messages")]
+        public List<FileMessage> Messages { get; set; }
+
+        /// <summary>
+        /// 温度参数
+        /// </summary>
+        [JsonPropertyName("temperature")]
+        public float Temperature { get; set; }
+
+        /// <summary>
+        /// 最大token数
+        /// </summary>
+        [JsonPropertyName("max_tokens")]
+        public int MaxTokens { get; set; }
+    }
+
+    /// <summary>
+    /// 包含文件引用的消息
+    /// </summary>
+    public class FileMessage
+    {
+        /// <summary>
+        /// 消息角色
+        /// </summary>
+        [JsonPropertyName("role")]
+        public string Role { get; set; }
+
+        /// <summary>
+        /// 消息内容
+        /// </summary>
+        [JsonPropertyName("content")]
+        public object Content { get; set; }
+    }
+
+    /// <summary>
+    /// 文件内容块
+    /// </summary>
+    public class FileContent
+    {
+        /// <summary>
+        /// 内容类型
+        /// </summary>
+        [JsonPropertyName("type")]
+        public string Type { get; set; } = "text";
+
+        /// <summary>
+        /// 文本内容
+        /// </summary>
+        [JsonPropertyName("text")]
+        public string Text { get; set; }
+    }
+
+    /// <summary>
+    /// API响应结果
+    /// </summary>
+    public class ApiResponse
+    {
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 响应消息
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 文件上传结果
+        /// </summary>
+        public List<FileUploadResult> FileResults { get; set; } = new List<FileUploadResult>();
+
+        /// <summary>
+        /// 回答内容(如果包含提问)
+        /// </summary>
+        public string Answer { get; set; }
+
+        /// <summary>
+        /// 使用的token数量
+        /// </summary>
+        public int TokensUsed { get; set; }
+    }
+
+    /// <summary>
+    /// 文件上传结果
+    /// </summary>
+    public class FileUploadResult
+    {
+        /// <summary>
+        /// 文件名
+        /// </summary>
+        public string FileName { get; set; }
+
+        /// <summary>
+        /// 文件ID
+        /// </summary>
+        public string FileId { get; set; }
+
+        /// <summary>
+        /// 文件大小
+        /// </summary>
+        public long FileSize { get; set; }
+
+        /// <summary>
+        /// 是否上传成功
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 状态消息
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 文件状态
+        /// </summary>
+        public string Status { get; set; }
+    }
+
+    /// <summary>
+    /// 文件删除请求
+    /// </summary>
+    public class DeleteFileRequest
+    {
+        /// <summary>
+        /// 要删除的文件ID
+        /// </summary>
+        public string FileId { get; set; }
+    }
+
+    /// <summary>
+    /// 项目文件读取请求
+    /// </summary>
+    public class ProjectFileReadRequest
+    {
+        /// <summary>
+        /// 要读取的文件相对路径列表(相对于项目根目录)
+        /// </summary>
+        public List<string> FilePaths { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 是否递归读取子目录
+        /// </summary>
+        public bool Recursive { get; set; } = false;
+
+        /// <summary>
+        /// 文件搜索模式(例如:*.cs)
+        /// </summary>
+        public string SearchPattern { get; set; } = "*.cs";
+
+        /// <summary>
+        /// 文件用途
+        /// </summary>
+        public string Purpose { get; set; } = "assistants";
+    }
+
+    /// <summary>
+    /// 项目文件信息
+    /// </summary>
+    public class ProjectFileInfo
+    {
+        /// <summary>
+        /// 文件相对路径
+        /// </summary>
+        public string RelativePath { get; set; }
+
+        /// <summary>
+        /// 文件完整路径
+        /// </summary>
+        public string FullPath { get; set; }
+
+        /// <summary>
+        /// 文件大小(字节)
+        /// </summary>
+        public long FileSize { get; set; }
+
+        /// <summary>
+        /// 最后修改时间
+        /// </summary>
+        public DateTime LastModified { get; set; }
+
+        /// <summary>
+        /// 是否成功读取
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 错误信息(如果有)
+        /// </summary>
+        public string ErrorMessage { get; set; }
+
+        /// <summary>
+        /// 转换后的IFormFile(如果成功)
+        /// </summary>
+        public IFormFile FormFile { get; set; }
+    }
+
+    /// <summary>
+    /// 项目文件读取响应
+    /// </summary>
+    public class ProjectFileReadResponse
+    {
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 响应消息
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 读取的文件信息列表
+        /// </summary>
+        public List<ProjectFileInfo> FileInfos { get; set; } = new List<ProjectFileInfo>();
+
+        /// <summary>
+        /// 成功读取的文件数量
+        /// </summary>
+        public int SuccessCount => FileInfos.Count(f => f.Success);
+
+        /// <summary>
+        /// 失败的文件数量
+        /// </summary>
+        public int FailureCount => FileInfos.Count(f => !f.Success);
+    }
+}

+ 636 - 0
OASystem/OASystem.Api/OAMethodLib/DeepSeekAPI/DeepSeekService.cs

@@ -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
+
+}

+ 137 - 0
OASystem/OASystem.Api/OAMethodLib/DeepSeekAPI/IDeepSeekService.cs

@@ -0,0 +1,137 @@
+namespace OASystem.API.OAMethodLib.DeepSeekAPI
+{
+    /// <summary>
+    /// DeepSeek API 服务接口
+    /// </summary>
+    public interface IDeepSeekService
+    {
+        /// <summary>
+        /// 测试API连通性
+        /// </summary>
+        /// <returns></returns>
+        Task<bool> TestApiConnectivityAsync();
+
+        /// <summary>
+        /// 检查可用端点
+        /// </summary>
+        /// <returns></returns>
+        Task<List<string>> DiscoverAvailableEndpointsAsync();
+
+        /// <summary>
+        /// 上传文件到DeepSeek API
+        /// </summary>
+        /// <param name="file">要上传的文件</param>
+        /// <param name="purpose">文件用途</param>
+        /// <returns>文件上传响应</returns>
+        Task<DeepSeekFileUploadResponse> UploadFileAsync(IFormFile file, string purpose = "assistants");
+
+        /// <summary>
+        /// 批量上传文件
+        /// </summary>
+        /// <param name="files">文件列表</param>
+        /// <param name="purpose">文件用途</param>
+        /// <returns>上传结果列表</returns>
+        Task<List<FileUploadResult>> UploadFilesAsync(List<IFormFile> files, string purpose = "assistants");
+
+        /// <summary>
+        /// 获取文件列表
+        /// </summary>
+        /// <returns>文件列表响应</returns>
+        Task<DeepSeekFileListResponse> ListFilesAsync();
+
+        /// <summary>
+        /// 获取文件信息
+        /// </summary>
+        /// <param name="fileId">文件ID</param>
+        /// <returns>文件信息</returns>
+        Task<DeepSeekFileUploadResponse> GetFileInfoAsync(string fileId);
+
+        /// <summary>
+        /// 删除文件
+        /// </summary>
+        /// <param name="fileId">文件ID</param>
+        /// <returns>是否删除成功</returns>
+        Task<bool> DeleteFileAsync(string fileId);
+
+        /// <summary>
+        /// 使用已上传的文件进行聊天
+        /// </summary>
+        /// <param name="fileIds">文件ID列表</param>
+        /// <param name="question">问题</param>
+        /// <param name="model">模型名称</param>
+        /// <param name="temperature">温度参数</param>
+        /// <param name="maxTokens">最大token数</param>
+        /// <returns>聊天响应</returns>
+        Task<ApiResponse> ChatWithFilesAsync(List<string> fileIds, string question, string model = "deepseek-chat", float temperature = 0.7f, int maxTokens = 4000);
+
+        /// <summary>
+        /// 等待文件处理完成
+        /// </summary>
+        /// <param name="fileId">文件ID</param>
+        /// <param name="timeoutSeconds">超时时间(秒)</param>
+        /// <returns>处理后的文件状态</returns>
+        Task<DeepSeekFileUploadResponse> WaitForFileProcessingAsync(string fileId, int timeoutSeconds = 60);
+
+        #region 本地项目相关
+        /// <summary>
+        /// 读取项目内的指定文件并转换为IFormFile
+        /// </summary>
+        /// <param name="relativePaths">文件相对路径列表</param>
+        /// <returns>文件读取响应</returns>
+        Task<ProjectFileReadResponse> ReadProjectFilesAsync(List<string> relativePaths);
+
+        /// <summary>
+        /// 读取项目内指定目录的文件
+        /// </summary>
+        /// <param name="directoryPath">目录相对路径</param>
+        /// <param name="searchPattern">文件搜索模式</param>
+        /// <param name="recursive">是否递归搜索</param>
+        /// <returns>文件读取响应</returns>
+        Task<ProjectFileReadResponse> ReadProjectDirectoryAsync(string directoryPath, string searchPattern = "*.cs", bool recursive = false);
+
+        /// <summary>
+        /// 获取项目根目录路径
+        /// </summary>
+        /// <returns>项目根目录完整路径</returns>
+        string GetProjectRootPath();
+
+        /// <summary>
+        /// 检查文件是否存在
+        /// </summary>
+        /// <param name="relativePath">文件相对路径</param>
+        /// <returns>是否存在</returns>
+        bool FileExists(string relativePath);
+
+        /// <summary>
+        /// 将物理文件转换为IFormFile
+        /// </summary>
+        /// <param name="filePath">文件完整路径</param>
+        /// <returns>IFormFile实例</returns>
+        Task<IFormFile> ConvertToFormFileAsync(string filePath);
+
+        /// <summary>
+        /// 读取特定的签证申请表单文件
+        /// </summary>
+        /// <param name="fileName"></param>
+        /// <returns></returns>
+        Task<ProjectFileReadResponse> ReadVisaFormFileAsync(string fileName);
+
+        /// <summary>
+        /// 读取OASystem项目中的所有CS文件
+        /// </summary>
+        Task<ProjectFileReadResponse> ReadOASystemFilesAsync(string searchPattern = "*.cs", bool recursive = true);
+
+        /// <summary>
+        /// 读取指定域模型中的CS文件
+        /// </summary>
+        Task<ProjectFileReadResponse> ReadDomainViewModelsAsync(string searchPattern = "*.cs", bool recursive = true);
+
+        /// <summary>
+        /// 读取签证表单相关的所有CS文件
+        /// </summary>
+        Task<ProjectFileReadResponse> ReadVisaFormFilesAsync(string searchPattern = "*.cs", bool recursive = true);
+
+        #endregion
+
+    }
+}

+ 385 - 0
OASystem/OASystem.Api/OAMethodLib/FileProcessing/FileProcessingService.cs

@@ -0,0 +1,385 @@
+
+using Aspose.Words;
+using Aspose.Words.Tables;
+using Aspose.Words.Fields;
+using System.Text;
+
+namespace OASystem.API.OAMethodLib.FileProcessing
+{
+    /// <summary>
+    /// 文件处理服务,使用Aspose.Words处理Word文档
+    /// </summary>
+    public class FileProcessingService : IFileProcessingService
+    {
+        private readonly ILogger<FileProcessingService> _logger;
+        private readonly IConfiguration _configuration;
+
+        public FileProcessingService(
+            ILogger<FileProcessingService> logger,
+            IConfiguration configuration)
+        {
+            _logger = logger;
+            _configuration = configuration;
+            InitializeAsposeLicense();
+        }
+
+        /// <summary>
+        /// 初始化Aspose.Words许可证
+        /// </summary>
+        private void InitializeAsposeLicense()
+        {
+            try
+            {
+                var licensePath = _configuration["Aspose:LicensePath"];
+                if (!string.IsNullOrEmpty(licensePath) && System.IO.File.Exists(licensePath))
+                {
+                    var license = new License();
+                    license.SetLicense(licensePath);
+                    _logger.LogInformation("Aspose.Words 15.12.0 许可证已成功加载");
+                }
+                else
+                {
+                    _logger.LogWarning("Aspose.Words许可证文件未找到,将使用评估模式");
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Aspose.Words许可证初始化失败");
+            }
+        }
+
+        /// <summary>
+        /// 处理Word文档并提取结构化信息
+        /// </summary>
+        public async Task<ProcessingResult> ProcessWordDocumentAsync(IFormFile file)
+        {
+            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+            try
+            {
+                _logger.LogInformation("开始处理Word文档: {FileName} ({Size} bytes)",
+                    file.FileName, file.Length);
+
+                if (!IsSupportedWordFormat(file.FileName))
+                {
+                    return new ProcessingResult
+                    {
+                        Success = false,
+                        ErrorMessage = $"不支持的文件格式: {Path.GetExtension(file.FileName)}",
+                        FileSize = file.Length,
+                        FileType = file.ContentType
+                    };
+                }
+
+                using var stream = new MemoryStream();
+                await file.CopyToAsync(stream);
+                stream.Position = 0;
+
+                var documentInfo = await ExtractDocumentInfoAsync(stream, file.FileName);
+
+                stopwatch.Stop();
+
+                _logger.LogInformation("Word文档处理完成: {FileName}, 耗时: {ElapsedMs}ms",
+                    file.FileName, stopwatch.ElapsedMilliseconds);
+
+                return new ProcessingResult
+                {
+                    Success = true,
+                    Data = documentInfo,
+                    FileSize = file.Length,
+                    FileType = file.ContentType
+                };
+            }
+            catch (Exception ex)
+            {
+                stopwatch.Stop();
+                _logger.LogError(ex, "Word文档处理失败: {FileName}", file.FileName);
+
+                return new ProcessingResult
+                {
+                    Success = false,
+                    ErrorMessage = $"处理失败: {ex.Message}",
+                    FileSize = file.Length,
+                    FileType = file.ContentType
+                };
+            }
+        }
+
+        /// <summary>
+        /// 批量处理Word文档
+        /// </summary>
+        public async Task<List<ProcessingResult>> ProcessWordDocumentsAsync(List<IFormFile> files)
+        {
+            var results = new List<ProcessingResult>();
+            var tasks = files.Select(ProcessWordDocumentAsync).ToList();
+
+            var batchResults = await Task.WhenAll(tasks);
+            results.AddRange(batchResults);
+
+            return results;
+        }
+
+        /// <summary>
+        /// 从流中提取文档信息
+        /// </summary>
+        public async Task<WordDocumentInfo> ExtractDocumentInfoAsync(Stream stream, string fileName)
+        {
+            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+            try
+            {
+                // 加载Word文档
+                var doc = new Document(stream);
+
+                var documentInfo = new WordDocumentInfo
+                {
+                    Title = ExtractDocumentTitle(doc),
+                    Content = ExtractTextContent(doc),
+                    Metadata = ExtractMetadata(doc),
+                    Tables = ExtractTables(doc),
+                    FormFields = ExtractFormFields(doc),
+                    Sections = ExtractSections(doc),
+                    ImagesCount = CountImages(doc)
+                };
+
+                stopwatch.Stop();
+                documentInfo.ProcessingTimeMs = stopwatch.ElapsedMilliseconds;
+
+                return documentInfo;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "文档信息提取失败: {FileName}", fileName);
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// 提取文档标题
+        /// </summary>
+        private string ExtractDocumentTitle(Document doc)
+        {
+            try
+            {
+                // 首先尝试从文档属性获取标题
+                var title = doc.BuiltInDocumentProperties.Title;
+                if (!string.IsNullOrEmpty(title))
+                    return title;
+
+                // 如果没有标题,尝试从第一个段落提取
+                foreach (Aspose.Words.Paragraph paragraph in doc.GetChildNodes(NodeType.Paragraph, true))
+                {
+                    var text = paragraph.GetText().Trim();
+                    if (!string.IsNullOrEmpty(text) && text.Length < 100) // 假设标题不会太长
+                        return text;
+                }
+
+                return "未命名文档";
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取文档标题失败");
+                return "未命名文档";
+            }
+        }
+
+        /// <summary>
+        /// 提取文本内容
+        /// </summary>
+        private string ExtractTextContent(Document doc)
+        {
+            try
+            {
+                // 使用Aspose.Words的GetText方法提取纯文本
+                return doc.GetText();
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取文本内容失败");
+                return string.Empty;
+            }
+        }
+
+        /// <summary>
+        /// 提取文档元数据
+        /// </summary>
+        private DocumentMetadata ExtractMetadata(Document doc)
+        {
+            try
+            {
+                var props = doc.BuiltInDocumentProperties;
+
+                return new DocumentMetadata
+                {
+                    Author = props.Author ?? string.Empty,
+                    Company = props.Company ?? string.Empty,
+                    CreatedTime = props.CreatedTime,
+                    LastSavedTime = props.LastSavedTime,
+                    PageCount = doc.PageCount,
+                    WordCount = props.Words,
+                    CharacterCount = props.Characters,
+                    Subject = props.Subject ?? string.Empty,
+                    Keywords = props.Keywords ?? string.Empty
+                };
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取元数据失败");
+                return new DocumentMetadata();
+            }
+        }
+
+        /// <summary>
+        /// 提取表格数据
+        /// </summary>
+        private List<DocumentTable> ExtractTables(Document doc)
+        {
+            var tables = new List<DocumentTable>();
+
+            try
+            {
+                int tableIndex = 1;
+                foreach (Aspose.Words.Tables.Table table in doc.GetChildNodes(NodeType.Table, true))
+                {
+                    var docTable = new DocumentTable
+                    {
+                        TableName = $"表格_{tableIndex}"
+                    };
+
+                    // 提取表头(假设第一行是表头)
+                    if (table.Rows.Count > 0)
+                    {
+                        var firstRow = table.FirstRow;
+                        foreach (Cell cell in firstRow.Cells)
+                        {
+                            docTable.Headers.Add(cell.GetText().Trim());
+                        }
+                    }
+
+                    // 提取所有行数据
+                    foreach (Row row in table.Rows)
+                    {
+                        var rowData = new List<string>();
+                        foreach (Cell cell in row.Cells)
+                        {
+                            rowData.Add(cell.GetText().Trim());
+                        }
+                        docTable.Rows.Add(rowData);
+                    }
+
+                    tables.Add(docTable);
+                    tableIndex++;
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取表格数据失败");
+            }
+
+            return tables;
+        }
+
+        /// <summary>
+        /// 提取表单字段
+        /// </summary>
+        private List<FormField> ExtractFormFields(Document doc)
+        {
+            var formFields = new List<FormField>();
+
+            try
+            {
+                foreach (FormField formField in doc.Range.FormFields)
+                {
+                    formFields.Add(new FormField
+                    {
+                        Name = formField.Name,
+                        Type = formField.Type.ToString()
+                        //,
+                        //Value = formField.Result ?? string.Empty,
+                        //IsChecked = formField.Checked
+                    });
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取表单字段失败");
+            }
+
+            return formFields;
+        }
+
+        /// <summary>
+        /// 提取章节信息
+        /// </summary>
+        private List<DocumentSection> ExtractSections(Document doc)
+        {
+            var sections = new List<DocumentSection>();
+
+            try
+            {
+                int sectionIndex = 1;
+                foreach (Section section in doc.Sections)
+                {
+                    var docSection = new DocumentSection
+                    {
+                        SectionNumber = sectionIndex,
+                        Content = section.GetText(),
+                        ParagraphsCount = section.Body.Paragraphs.Count,
+                        TablesCount = section.Body.Tables.Count
+                    };
+
+                    sections.Add(docSection);
+                    sectionIndex++;
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "提取章节信息失败");
+            }
+
+            return sections;
+        }
+
+        /// <summary>
+        /// 统计图片数量
+        /// </summary>
+        private int CountImages(Document doc)
+        {
+            try
+            {
+                int imageCount = 0;
+                foreach (Aspose.Words.Drawing.Shape shape in doc.GetChildNodes(NodeType.Shape, true))
+                {
+                    if (shape.HasImage)
+                    {
+                        imageCount++;
+                    }
+                }
+                return imageCount;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogWarning(ex, "统计图片数量失败");
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 检查文件是否为支持的Word格式
+        /// </summary>
+        public bool IsSupportedWordFormat(string fileName)
+        {
+            var extension = Path.GetExtension(fileName).ToLower();
+            return extension switch
+            {
+                ".doc" => true,
+                ".docx" => true,
+                ".dot" => true,
+                ".dotx" => true,
+                ".docm" => true,
+                ".dotm" => true,
+                _ => false
+            };
+        }
+    }
+}

+ 26 - 0
OASystem/OASystem.Api/OAMethodLib/FileProcessing/IFileProcessingService.cs

@@ -0,0 +1,26 @@
+using Aspose.Words;
+namespace OASystem.API.OAMethodLib.FileProcessing
+{
+    public interface IFileProcessingService
+    {
+        /// <summary>
+        /// 处理Word文档并提取结构化信息
+        /// </summary>
+        Task<ProcessingResult> ProcessWordDocumentAsync(IFormFile file);
+
+        /// <summary>
+        /// 批量处理Word文档
+        /// </summary>
+        Task<List<ProcessingResult>> ProcessWordDocumentsAsync(List<IFormFile> files);
+
+        /// <summary>
+        /// 从流中提取文档信息
+        /// </summary>
+        Task<WordDocumentInfo> ExtractDocumentInfoAsync(Stream stream, string fileName);
+
+        /// <summary>
+        /// 检查文件是否为支持的Word格式
+        /// </summary>
+        bool IsSupportedWordFormat(string fileName);
+    }
+}

+ 143 - 0
OASystem/OASystem.Api/OAMethodLib/FileProcessing/WordDocumentModels.cs

@@ -0,0 +1,143 @@
+using System.Text.Json.Serialization;
+
+namespace OASystem.API.OAMethodLib.FileProcessing
+{
+    public class WordDocumentModels
+    {
+    }
+
+    /// <summary>
+    /// Word文档信息模型
+    /// </summary>
+    public class WordDocumentInfo
+    {
+        [JsonPropertyName("title")]
+        public string Title { get; set; } = string.Empty;
+
+        [JsonPropertyName("content")]
+        public string Content { get; set; } = string.Empty;
+
+        [JsonPropertyName("metadata")]
+        public DocumentMetadata Metadata { get; set; } = new DocumentMetadata();
+
+        [JsonPropertyName("tables")]
+        public List<DocumentTable> Tables { get; set; } = new List<DocumentTable>();
+
+        [JsonPropertyName("formFields")]
+        public List<FormField> FormFields { get; set; } = new List<FormField>();
+
+        [JsonPropertyName("sections")]
+        public List<DocumentSection> Sections { get; set; } = new List<DocumentSection>();
+
+        [JsonPropertyName("imagesCount")]
+        public int ImagesCount { get; set; }
+
+        [JsonPropertyName("processingTimeMs")]
+        public long ProcessingTimeMs { get; set; }
+    }
+
+    /// <summary>
+    /// 文档元数据
+    /// </summary>
+    public class DocumentMetadata
+    {
+        [JsonPropertyName("author")]
+        public string Author { get; set; } = string.Empty;
+
+        [JsonPropertyName("company")]
+        public string Company { get; set; } = string.Empty;
+
+        [JsonPropertyName("createdTime")]
+        public DateTime? CreatedTime { get; set; }
+
+        [JsonPropertyName("lastSavedTime")]
+        public DateTime? LastSavedTime { get; set; }
+
+        [JsonPropertyName("pageCount")]
+        public int PageCount { get; set; }
+
+        [JsonPropertyName("wordCount")]
+        public int WordCount { get; set; }
+
+        [JsonPropertyName("characterCount")]
+        public int CharacterCount { get; set; }
+
+        [JsonPropertyName("subject")]
+        public string Subject { get; set; } = string.Empty;
+
+        [JsonPropertyName("keywords")]
+        public string Keywords { get; set; } = string.Empty;
+    }
+
+    /// <summary>
+    /// 文档表格
+    /// </summary>
+    public class DocumentTable
+    {
+        [JsonPropertyName("tableName")]
+        public string TableName { get; set; } = string.Empty;
+
+        [JsonPropertyName("rows")]
+        public List<List<string>> Rows { get; set; } = new List<List<string>>();
+
+        [JsonPropertyName("headers")]
+        public List<string> Headers { get; set; } = new List<string>();
+    }
+
+    /// <summary>
+    /// 表单字段
+    /// </summary>
+    public class FormField
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; } = string.Empty;
+
+        [JsonPropertyName("type")]
+        public string Type { get; set; } = string.Empty;
+
+        [JsonPropertyName("value")]
+        public string Value { get; set; } = string.Empty;
+
+        [JsonPropertyName("isChecked")]
+        public bool IsChecked { get; set; }
+    }
+
+    /// <summary>
+    /// 文档章节
+    /// </summary>
+    public class DocumentSection
+    {
+        [JsonPropertyName("sectionNumber")]
+        public int SectionNumber { get; set; }
+
+        [JsonPropertyName("content")]
+        public string Content { get; set; } = string.Empty;
+
+        [JsonPropertyName("paragraphsCount")]
+        public int ParagraphsCount { get; set; }
+
+        [JsonPropertyName("tablesCount")]
+        public int TablesCount { get; set; }
+    }
+
+    /// <summary>
+    /// 处理结果
+    /// </summary>
+    public class ProcessingResult
+    {
+        [JsonPropertyName("success")]
+        public bool Success { get; set; }
+
+        [JsonPropertyName("data")]
+        public WordDocumentInfo? Data { get; set; }
+
+        [JsonPropertyName("errorMessage")]
+        public string? ErrorMessage { get; set; }
+
+        [JsonPropertyName("fileSize")]
+        public long FileSize { get; set; }
+
+        [JsonPropertyName("fileType")]
+        public string FileType { get; set; } = string.Empty;
+    }
+}

+ 17 - 18
OASystem/OASystem.Api/OAMethodLib/GeneralMethod.cs

@@ -6,9 +6,6 @@ using Aspose.Words.Saving;
 using Microsoft.AspNetCore.SignalR;
 using Microsoft.International.Converters.PinYinConverter;
 using NodaTime;
-using NodaTime.Extensions;
-using NodaTime.TimeZones;
-using OASystem.API.OAMethodLib.APNs;
 using OASystem.API.OAMethodLib.File;
 using OASystem.API.OAMethodLib.Hub.HubClients;
 using OASystem.API.OAMethodLib.Hub.Hubs;
@@ -25,8 +22,6 @@ using OASystem.Domain.ViewModels.Groups;
 using OASystem.Domain.ViewModels.JuHeExchangeRate;
 using OASystem.Infrastructure.Repositories.CRM;
 using OASystem.Infrastructure.Repositories.Groups;
-using OASystem.Infrastructure.Repositories.Login;
-using OfficeOpenXml;
 using System.Data;
 using System.IdentityModel.Tokens.Jwt;
 using System.Security.Claims;
@@ -43,7 +38,7 @@ namespace OASystem.API.OAMethodLib
         private static readonly SetDataRepository _setDataRep = AutofacIocManager.Instance.GetService<SetDataRepository>();
         private static readonly TableOperationRecordRepository _tableOperationRecordRep = AutofacIocManager.Instance.GetService<TableOperationRecordRepository>();
         private static readonly MessageRepository _messageRep = AutofacIocManager.Instance.GetService<MessageRepository>();
-        
+
         private static readonly IHubContext<ChatHub, IChatClient> _hubContext = AutofacIocManager.Instance.GetService<IHubContext<ChatHub, IChatClient>>();
 
         private readonly static string[] weekdays = new string[] { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
@@ -160,7 +155,7 @@ namespace OASystem.API.OAMethodLib
             {
                 pageData.AddRange(_defaultData.Find(it => it.DepId == 0)?.PostPageAuths[0].PageIds ?? new List<int>());
             }
-           
+
 
             if (pageData.Count > 0)
             {
@@ -1079,10 +1074,10 @@ namespace OASystem.API.OAMethodLib
         /// <param name="groupId"></param>
         /// <returns></returns>
         public static List<GroupOperationUserView> GetGroupModuleOperators(int groupId)
-        { 
+        {
             var modoule = new GroupOperationUserView();
             var view = modoule.GetOperationUsersInit();
-            if(groupId < 1) return view;
+            if (groupId < 1) return view;
             var groupInfo = _sqlSugar.Queryable<Grp_DelegationInfo>()
                 .Where(it => it.IsDel == 0 && it.Id == groupId)
                 .First();
@@ -1091,7 +1086,7 @@ namespace OASystem.API.OAMethodLib
             //获取团组下的操作人
             //车/导游地接
             var opUers = _sqlSugar.Queryable<Grp_CarTouristGuideGroundReservations>()
-                .InnerJoin<Grp_CreditCardPayment>((x,y) => x.Id == y.CId && y.CTable == 79)
+                .InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && y.CTable == 79)
                 .LeftJoin<Sys_Users>((x, y, u) => x.CreateUserId == u.Id)
                 .Where((x, y, u) => x.DiId == groupId && x.IsDel == 0)
                 .Select((x, y, u) => new GroupOperationUserInfo
@@ -2369,7 +2364,7 @@ namespace OASystem.API.OAMethodLib
                             return !excludeArr.Contains(x);
                         }).Count() != 9)
                         {
-                            dt.Rows.Add(null, null, null, null, null, null, null, null, null, null, null, "本团组第" + (i + 1) + "段黑屏代码中第" + (j + 1) + " 条有误,请联系机票同事核对", null, false,false);
+                            dt.Rows.Add(null, null, null, null, null, null, null, null, null, null, null, "本团组第" + (i + 1) + "段黑屏代码中第" + (j + 1) + " 条有误,请联系机票同事核对", null, false, false);
                             //MessageBoxEx.Show("第" + (i + 1) + "段黑屏代码中第" + (j + 1) + " 条有误, 请联系机票同事核对");
                             return dt;
                         }
@@ -4141,18 +4136,21 @@ namespace OASystem.API.OAMethodLib
             DateTime? currDt = null;
 
             var currTimezones = _timeZoneCityMappings.Where(x => x.Value.Contains(city)).ToList();
-            if (!currTimezones.Any()) {
+            if (!currTimezones.Any())
+            {
 
                 currTimezones = _timeZoneCityMappings.Where(x => x.Value.Contains(country)).ToList();
 
 
-            };
+            }
+            ;
             string timeZoneId = string.Empty;
             if (currTimezones.Count > 1)
             {
                 currTimezones = currTimezones.Where(x => x.Value.Contains(country)).ToList();
                 timeZoneId = currTimezones.FirstOrDefault().Key ?? string.Empty;
-            }else timeZoneId = currTimezones.FirstOrDefault().Key ?? string.Empty;
+            }
+            else timeZoneId = currTimezones.FirstOrDefault().Key ?? string.Empty;
 
             if (string.IsNullOrEmpty(timeZoneId)) return currDt;
 
@@ -4181,7 +4179,7 @@ namespace OASystem.API.OAMethodLib
         /// <param name="footerLabel">页脚内容</param>
         /// <param name="font">字体名称</param>
         /// <param name="fontSize"><字体大小/param>
-        public static void AsposeWordSetFooter(DocumentBuilder builder,string footerLabel,string font,int fontSize)
+        public static void AsposeWordSetFooter(DocumentBuilder builder, string footerLabel, string font, int fontSize)
         {
             Section currentSection = builder.CurrentSection;
             var pageSetup = currentSection.PageSetup;
@@ -4298,9 +4296,9 @@ namespace OASystem.API.OAMethodLib
         /// <param name="groupId"></param>
         /// <param name="checkCurrencys">已使用币种</param>
         /// <returns></returns>
-        public static async Task<(bool,string, List<CurrencyInfo>)> EnterExitCostCheckRate(int groupId,List<CurrencyInfo> checkCurrencys)
+        public static async Task<(bool, string, List<CurrencyInfo>)> EnterExitCostCheckRate(int groupId, List<CurrencyInfo> checkCurrencys)
         {
-            if (checkCurrencys == null || checkCurrencys.Count < 1) return (false, string.Empty,new List<CurrencyInfo>());
+            if (checkCurrencys == null || checkCurrencys.Count < 1) return (false, string.Empty, new List<CurrencyInfo>());
 
             var info = await _sqlSugar.Queryable<Grp_EnterExitCost>().FirstAsync(x => x.IsDel == 0 && x.DiId == groupId);
             if (info == null) return (false, string.Empty, new List<CurrencyInfo>());
@@ -5721,7 +5719,7 @@ namespace OASystem.API.OAMethodLib
                         var redisKeyName = string.Format("AirTripDesc_{0}_{1}", x.DiId, x.Id);
                         tripDesc = await KIMIAirTripCodeRec(redisKeyName, x.BlackCode);
                     }
-                    
+
 
                     result.Add(new EntryAndExitTipsView
                     {
@@ -5778,6 +5776,7 @@ namespace OASystem.API.OAMethodLib
 
         #endregion
         #endregion
+
     }
 }
 

+ 16 - 15
OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/AppNotice/Library.cs

@@ -5,9 +5,7 @@ using OASystem.Domain.Entities.PersonnelModule;
 using OASystem.Domain.ViewModels.CRM;
 using OASystem.Domain.ViewModels.Groups;
 using OASystem.Domain.ViewModels.QiYeWeChat;
-using OASystem.Domain.ViewModels.Statistics;
 using OASystem.Infrastructure.Repositories.Groups;
-using Quartz.Logging;
 
 namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
 {
@@ -891,15 +889,16 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
         /// </summary>
         /// <param name="diId"></param>
         /// <param name="operationId"></param>
+        /// <param name="msgContent"></param>
         /// <returns></returns>
-        public static async Task<bool> SendUserMsg_GroupShare_ToFin(int diId,int operationId, string msgContent)
+        public static async Task<bool> SendUserMsg_GroupShare_ToFin(int diId, int operationId, string msgContent)
         {
             Grp_DelegationInfo groupInfo = _grpDeleRep.Query<Grp_DelegationInfo>(s => s.Id == diId).First();
 
             var operationName = _grpDeleRep.Query<Sys_Users>(s => s.IsDel == 0 && s.Id == operationId).First()?.CnName ?? "Unknown";
-           
+
             var defaultUserIds = new List<string>() { "208", "22", "5" };
-            
+
             if (defaultUserIds.Count > 0)
             {
                 List<string> qwUserIdList = GetQiyeChatUserIdList(defaultUserIds);
@@ -911,7 +910,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
 >**汇率明细**   
 {1}
 
->操作人员:<font color='comment'>{2}</font> 
+>操作人员:<font color='comment'>{2}</font> l
 >操作时间:<font color='comment'>{3}</font> 
 
 [详细信息请前往OA系统查看](http://oa.pan-american-intl.com:4399/)   ", groupInfo.TeamName, msgContent, operationName, DateTime.Now.ToString("yyyy-MM-dd HH:mm"));
@@ -1241,7 +1240,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
         /// <param name="diId"></param>
         /// <param name="operationId"></param>
         /// <returns></returns>
-        public static async Task<bool> SendUserMsg_CompanyShare_ToHR(int dataId,int diId, int operationId)
+        public static async Task<bool> SendUserMsg_CompanyShare_ToHR(int dataId, int diId, int operationId)
         {
             //企微通知
             var userIds = new List<string>()
@@ -1261,10 +1260,11 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
                     .Queryable<Sys_AuditTemplateNode>()
                     .First(x => x.IsDel == 0 && x.TemplateId == flow.TemplateId && x.Id == flow.CurrentNodeId);
                 if (auditNode != null)
-                { 
+                {
                     var auditUserList = _grpDeleRep._sqlSugar.Queryable<Sys_AuditTemplateNodeUser>()
                         .Where(x => x.IsDel == 0 && x.NodeId == auditNode.Id)
-                        .Select(x => new {
+                        .Select(x => new
+                        {
                             x.UserId,
                             x.UserName
                         })
@@ -1275,7 +1275,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
 
                         userIds.AddRange(auditUserList.Select(x => x.UserId.ToString()).ToList());
                         userIds = userIds.Distinct().ToList();
-                        valuableUserNames.AddRange( auditUserList.Select(x => x.UserName).ToList());
+                        valuableUserNames.AddRange(auditUserList.Select(x => x.UserName).ToList());
                     }
                 }
             }
@@ -1296,9 +1296,10 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
             if (receiveInfo == null) return false;
             var receiveDetails = _grpDeleRep._sqlSugar
                 .Queryable<Pm_GoodsReceiveDetails>()
-                .LeftJoin<Pm_GoodsInfo>((x,y) => x.GoodsId == y.Id)
+                .LeftJoin<Pm_GoodsInfo>((x, y) => x.GoodsId == y.Id)
                 .Where(x => x.IsDel == 0 && x.GoodsReceiveId == dataId)
-                .Select((x,y) => new { 
+                .Select((x, y) => new
+                {
                     y.Name,
                     x.Quantity,
                     y.Unit
@@ -1367,7 +1368,7 @@ DateTime.Now.ToString("yyyy-MM-dd HH:mm"));
 [详细信息请前往OA系统查看](http://oa.pan-american-intl.com:4399/)   ",
 auditUserLabel,
 operationName,
-valuableUserNames.Count - 1 ,
+valuableUserNames.Count - 1,
 groupLabel,
 goodsMsg,
 receiveInfo.Reason,
@@ -1395,12 +1396,12 @@ DateTime.Now.ToString("yyyy-MM-dd HH:mm"));
         /// <param name="operationId">操作人Id</param>
         /// <param name="recipients">接收者Id集合</param>
         /// <returns></returns>
-        public static async Task<bool> SendUserMsg_CompanyShare_ToDailtPay(int dataId, int operationId,List<string> recipients)
+        public static async Task<bool> SendUserMsg_CompanyShare_ToDailtPay(int dataId, int operationId, List<string> recipients)
         {
             //企微通知
             var userIds = recipients;
 
-            string operationName = _grpDeleRep._sqlSugar.Queryable<Sys_Users>().First(x => x.IsDel 
+            string operationName = _grpDeleRep._sqlSugar.Queryable<Sys_Users>().First(x => x.IsDel
              == 0 && x.Id == operationId).CnName ?? "Unknown";
 
             var dailyPaymentInfo = await _grpDeleRep._sqlSugar.Queryable<Fin_DailyFeePayment>()

+ 9 - 1
OASystem/OASystem.Api/Program.cs

@@ -7,8 +7,10 @@ using OASystem.API.Middlewares;
 using OASystem.API.OAMethodLib;
 using OASystem.API.OAMethodLib.AMapApi;
 using OASystem.API.OAMethodLib.APNs;
+using OASystem.API.OAMethodLib.DeepSeekAPI;
 using OASystem.API.OAMethodLib.Hub.Hubs;
 using OASystem.API.OAMethodLib.JuHeAPI;
+using OASystem.API.OAMethodLib.Logging;
 using OASystem.API.OAMethodLib.QiYeWeChatAPI;
 using OASystem.API.OAMethodLib.Quartz.Jobs;
 using OASystem.API.OAMethodLib.SignalR.HubService;
@@ -19,7 +21,6 @@ using QuzrtzJob.Factory;
 using Serilog.Events;
 using System.Diagnostics;
 using System.IO.Compression;
-using OASystem.API.OAMethodLib.Logging;
 
 Console.Title = $"FMGJ OASystem Server";
 var builder = WebApplication.CreateBuilder(args);
@@ -415,6 +416,13 @@ builder.Services.AddScoped<IMapper, Mapper>();
 
 #endregion
 
+#region DeepSeek AI 服务
+
+// 配置HTTP客户端
+builder.Services.AddHttpClient<IDeepSeekService, DeepSeekService>();
+
+#endregion
+
 #region 聚合API 服务
 builder.Services.AddControllersWithViews();
 builder.Services.AddSingleton<IJuHeApiService, JuHeApiService>();

+ 4 - 0
OASystem/OASystem.Api/appsettings.json

@@ -457,6 +457,10 @@
     "Key": "sk-AY1Sv4rLnSUgGGHcC8SGSWYYKzGID7leZJcFfxAYozLC8dIc"
     //"Key": "sk-b6jgRbgU4ZtIFLkgMSjtHeU67q8OHzPTqIVuKuNLG50zd5IY" //leiyi kimi key
   },
+  "DeepSeek": {
+    "BaseAddress": "https://api.deepseek.com/v1/",
+    "ApiKey": "sk-a0415069ef56478aaa3028d779d1ace9"
+  },
   "OverspendAuditUser": [
     {
       "Job": "syZhuGuan",

+ 266 - 0
OASystem/OASystem.Domain/ViewModels/CRM/VisaClientDetails.cs

@@ -0,0 +1,266 @@
+namespace OASystem.Domain.ViewModels.CRM
+{
+    /// <summary>
+    /// 签证申请类
+    /// </summary>
+    public class VisaClientDetails
+    {
+        ///<summary>个人信息</summary>
+        public PersonalInfo PersonalInfo { get; set; }
+        ///<summary>教育经历列表</summary>
+        public List<Education> EducationList { get; set; }
+        ///<summary>工作经历列表</summary>
+        public List<WorkExperience> WorkExperienceList { get; set; }
+        ///<summary>旅行历史</summary>
+        public List<string> TravelHistory { get; set; }
+        ///<summary>签证类型</summary>
+        public string VisaType { get; set; }
+        ///<summary>签证签发日期</summary>
+        public string VisaIssueDate { get; set; }
+        ///<summary>签证有效期</summary>
+        public string VisaExpiryDate { get; set; }
+        ///<summary>是否有指纹记录</summary>
+        public bool HasFingerprints { get; set; }
+        ///<summary>是否去过申根国家</summary>
+        public bool HasBeenToSchengen { get; set; }
+        ///<summary>申根签证详情列表</summary>
+        public List<string> SchengenVisaDetails { get; set; }
+        ///<summary>是否被申根国家拒签</summary>
+        public bool HasBeenRefusedSchengenVisa { get; set; }
+        ///<summary>申根签证拒签详情</summary>
+        public string SchengenVisaRefusalDetails { get; set; }
+        ///<summary>签证目的</summary>
+        public string VisaPurpose { get; set; }
+        ///<summary>申请入境次数</summary>
+        public string VisaEntryTimes { get; set; }
+        ///<summary>计划出发日期</summary>
+        public string PlannedDepartureDate { get; set; }
+        ///<summary>计划回国日期</summary>
+        public string PlannedReturnDate { get; set; }
+        ///<summary>签证费用支付方</summary>
+        public string VisaFeePayer { get; set; }
+        ///<summary>签证费用支付方式</summary>
+        public string VisaFeePaymentMethod { get; set; }
+        ///<summary>赞助人姓名</summary>
+        public string SponsorName { get; set; }
+        ///<summary>赞助人地址</summary>
+        public string SponsorAddress { get; set; }
+        ///<summary>赞助人国籍</summary>
+        public string SponsorNationality { get; set; }
+        ///<summary>赞助人电话</summary>
+        public string SponsorPhone { get; set; }
+        ///<summary>赞助人邮箱</summary>
+        public string SponsorEmail { get; set; }
+    }
+    
+    /// <summary>
+    /// 拒签信息类
+    /// </summary>
+    public class RefusalInfo
+    {
+        ///<summary>拒签国家</summary>
+        public string Country { get; set; }
+        ///<summary>拒签日期</summary>
+        public string Date { get; set; }
+        ///<summary>拒签地点</summary>
+        public string Location { get; set; }
+        ///<summary>申请签证类型</summary>
+        public string VisaType { get; set; }
+        ///<summary>拒签原因</summary>
+        public string Reason { get; set; }
+    }
+
+    /// <summary>
+    /// 参军信息类
+    /// </summary>
+    public class MilitaryServiceInfo
+    {
+        ///<summary>军种</summary>
+        public string Branch { get; set; }
+        ///<summary>军衔</summary>
+        public string Rank { get; set; }
+        ///<summary>军事特长</summary>
+        public string Specialty { get; set; }
+        ///<summary>服役开始日期</summary>
+        public string StartDate { get; set; }
+        ///<summary>服役结束日期</summary>
+        public string EndDate { get; set; }
+    }
+
+    /// <summary>
+    /// 家庭成员信息类
+    /// </summary>
+    public class FamilyMemberInfo
+    {
+        ///<summary>关系</summary>
+        public string Relationship { get; set; }
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>出生地</summary>
+        public string BirthPlace { get; set; }
+        ///<summary>现国籍</summary>
+        public string CurrentNationality { get; set; }
+        ///<summary>出生国</summary>
+        public string CountryOfBirth { get; set; }
+        ///<summary>婚姻状况</summary>
+        public string MaritalStatus { get; set; }
+        ///<summary>地址</summary>
+        public string Address { get; set; }
+        ///<summary>联系方式</summary>
+        public string Contact { get; set; }
+        ///<summary>现职务</summary>
+        public string CurrentOccupation { get; set; }
+        ///<summary>现居住国</summary>
+        public string Nationality { get; set; }
+        ///<summary>护照号码(如有)</summary>
+        public string PassportNumber { get; set; }
+        ///<summary>签证状态(如有)</summary>
+        public string VisaStatus { get; set; }
+    }
+
+    /// <summary>
+    /// 新西兰/澳大利亚联系人信息类
+    /// </summary>
+    public class NzAuContactInfo
+    {
+        ///<summary>关系</summary>
+        public string Relationship { get; set; }
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>地址</summary>
+        public string Address { get; set; }
+        ///<summary>联系方式</summary>
+        public string Contact { get; set; }
+    }
+
+    // 个人信息类
+    public class PersonalInfo
+    {
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>拼音名</summary>
+        public string PinyinName { get; set; }
+        ///<summary>别名</summary>
+        public string Alias { get; set; }
+        ///<summary>性别</summary>
+        public string Gender { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>出生地</summary>
+        public string BirthPlace { get; set; }
+        ///<summary>身份证号码</summary>
+        public string IdNumber { get; set; }
+        ///<summary>婚姻状况</summary>
+        public string MaritalStatus { get; set; }
+        ///<summary>配偶姓名</summary>
+        public string SpouseName { get; set; }
+        ///<summary>配偶出生日期</summary>
+        public string SpouseBirthDate { get; set; }
+        ///<summary>配偶出生地</summary>
+        public string SpouseBirthPlace { get; set; }
+        ///<summary>家庭地址</summary>
+        public string FamilyAddress { get; set; }
+        ///<summary>家庭电话</summary>
+        public string FamilyPhone { get; set; }
+        ///<summary>移动电话</summary>
+        public string MobilePhone { get; set; }
+        ///<summary>电子邮件</summary>
+        public string Email { get; set; }
+        ///<summary>护照号码</summary>
+        public string PassportNumber { get; set; }
+        ///<summary>护照签发地</summary>
+        public string PassportIssuePlace { get; set; }
+        ///<summary>美国社会安全号或纳税ID号</summary>
+        public string SocialSecurityNumber { get; set; }
+        ///<summary>是否有驾照</summary>
+        public bool HasDriversLicense { get; set; }
+        ///<summary>驾照号码</summary>
+        public string DriversLicenseNumber { get; set; }
+        ///<summary>驾照签发地</summary>
+        public string DriversLicenseIssuePlace { get; set; }
+        ///<summary>是否是其他国家的永久居民</summary>
+        public bool IsPermanentResident { get; set; }
+        ///<summary>是否使用过社交账号</summary>
+        public bool HasSocialMediaAccounts { get; set; }
+        ///<summary>社交账号列表</summary>
+        public List<string> SocialMediaAccounts { get; set; }
+        ///<summary>是否丢失过护照</summary>
+        public bool HasLostPassport { get; set; }
+        ///<summary>丢失护照号码</summary>
+        public string LostPassportNumber { get; set; }
+        ///<summary>是否曾被拒签</summary>
+        public bool HasBeenRefusedVisa { get; set; }
+        ///<summary>拒签信息列表</summary>
+        public List<RefusalInfo> RefusalInfos { get; set; }
+        ///<summary>是否申请过美国移民签证</summary>
+        public bool HasImmigrantVisaApplication { get; set; }
+        ///<summary>移民签证申请详情</summary>
+        public string ImmigrantVisaApplicationDetails { get; set; }
+        ///<summary>是否有直系亲属在美国</summary>
+        public bool HasRelativesInUsa { get; set; }
+        ///<summary>美国直系亲属详情列表</summary>
+        public List<string> RelativesInUsaDetails { get; set; }
+        ///<summary>是否有旁系亲属在美国</summary>
+        public bool HasDistantRelativesInUsa { get; set; }
+        ///<summary>美国旁系亲属详情列表</summary>
+        public List<string> DistantRelativesInUsaDetails { get; set; }
+        ///<summary>是否参过军</summary>
+        public bool HasMilitaryService { get; set; }
+        ///<summary>参军信息列表</summary>
+        public List<MilitaryServiceInfo> MilitaryServiceInfos { get; set; }
+        ///<summary>家庭成员信息列表</summary>
+        public List<FamilyMemberInfo> FamilyMembers { get; set; }
+        ///<summary>在新西兰/澳大利亚是否有亲属、朋友或联系人</summary>
+        public List<NzAuContactInfo> NzAuContacts { get; set; }
+        ///<summary>父亲姓名</summary>
+        public string FatherName { get; set; }
+        ///<summary>父亲出生日期</summary>
+        public string FatherBirthDate { get; set; }
+        ///<summary>母亲姓名</summary>
+        public string MotherName { get; set; }
+        ///<summary>母亲出生日期</summary>
+        public string MotherBirthDate { get; set; }
+    }
+
+    // 教育经历类
+    public class Education
+    {
+        ///<summary>学校名称</summary>
+        public string SchoolName { get; set; }
+        ///<summary>学校地址</summary>
+        public string SchoolAddress { get; set; }
+        ///<summary>学位</summary>
+        public string Degree { get; set; }
+        ///<summary>专业</summary>
+        public string Major { get; set; }
+        ///<summary>入学日期</summary>
+        public string EnrollmentDate { get; set; }
+        ///<summary>毕业日期</summary>
+        public string GraduationDate { get; set; }
+    }
+
+    // 工作经历类
+    public class WorkExperience
+    {
+        ///<summary>公司名称</summary>
+        public string CompanyName { get; set; }
+        ///<summary>公司地址</summary>
+        public string CompanyAddress { get; set; }
+        ///<summary>职位</summary>
+        public string Position { get; set; }
+        ///<summary>开始日期</summary>
+        public string StartDate { get; set; }
+        ///<summary>结束日期</summary>
+        public string EndDate { get; set; }
+        ///<summary>上级领导姓名</summary>
+        public string SupervisorName { get; set; }
+        ///<summary>上级领导电话</summary>
+        public string SupervisorPhone { get; set; }
+    }
+
+    
+}

+ 8 - 8
OASystem/OASystem.Domain/ViewModels/CRM/VisaDeleClientView.cs

@@ -478,11 +478,11 @@ public class VisaApplication
         [JsonPropertyName("新西兰澳大利亚访问记录")]
         public List<VisitHistory> VisitHistory { get; set; }
 
-        /// <summary>
-        /// 拒签记录
-        /// </summary>
-        [JsonPropertyName("拒签记录")]
-        public List<RejectionRecord> RejectionRecords { get; set; }
+        ///// <summary>
+        ///// 拒签记录
+        ///// </summary>
+        //[JsonPropertyName("拒签记录")]
+        //public List<RejectionRecord> RejectionRecords { get; set; }
 
         /// <summary>
         /// 教育经历
@@ -494,7 +494,7 @@ public class VisaApplication
         /// 工作经历
         /// </summary>
         [JsonPropertyName("工作经历")]
-        public List<WorkExperience> WorkExperiences { get; set; }
+        public List<WorkExperiences> WorkExperiences { get; set; }
 
         /// <summary>
         /// 服兵役历史(识别成一段话)
@@ -665,7 +665,7 @@ public class VisaApplication
     /// <summary>
     /// 拒签记录
     /// </summary>
-    public class RejectionRecord
+    public class RejectionRecord1
     {
         /// <summary>
         /// 国家
@@ -725,7 +725,7 @@ public class VisaApplication
     /// <summary>
     /// 工作经历
     /// </summary>
-    public class WorkExperience
+    public class WorkExperiences
     {
         /// <summary>
         /// 单位名称

+ 182 - 0
OASystem/OASystem.Domain/ViewModels/VisaFormDetails/AusNewVisaApplicationForm.cs

@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.ViewModels.VisaFormDetails
+{
+    /// <summary>
+    /// 澳大利亚新签证申请表
+    /// </summary>
+    public class AusNewVisaApplicationForm
+    {
+        // 是否曾经去过其他国家
+        ///<summary>是否曾经去过其他国家</summary>
+        public List<CountryVisit> CountryVisits { get; set; }
+
+        // 申请人的个人信息
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>曾用名</summary>
+        public string Alias { get; set; }
+        ///<summary>婚姻状况</summary>
+        public string MaritalStatus { get; set; }
+        ///<summary>出生地</summary>
+        public string BirthPlace { get; set; }
+        ///<summary>性别</summary>
+        public string Gender { get; set; }
+        ///<summary>手机</summary>
+        public string Mobile { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>现在住址</summary>
+        public string CurrentAddress { get; set; }
+
+        ///<summary>在职人员信息</summary>
+        public EmploymentInfo EmploymentInfo { get; set; }
+
+        ///<summary>申请人家庭成员</summary>
+        public List<FamilyMember> FamilyMembers { get; set; }
+
+        ///<summary>关系信息</summary>
+        public List<Relation> Relations { get; set; }
+
+        ///<summary>拒签历史</summary>
+        public List<Rejection> Rejections { get; set; }
+
+        ///<summary>新西兰/澳大利亚旅行历史</summary>
+        public List<TravelHistory> TravelHistories { get; set; }
+
+        ///<summary>教育经历</summary>
+        public List<Education> Educations { get; set; }
+
+        ///<summary>工作经历</summary>
+        public List<WorkExperience> WorkExperiences { get; set; }
+
+        ///<summary>服兵役历史</summary>
+        public List<MilitaryService> MilitaryServices { get; set; }
+
+    }
+
+    public class CountryVisit
+    {
+        ///<summary>国家</summary>
+        public string Country { get; set; }
+        ///<summary>访问日期</summary>
+        public string VisitDate { get; set; }
+    }
+
+    public class TravelHistory
+    {
+        public int Order { get; set; } // 次序
+        public string ArrivalDate { get; set; }   // 抵达日期
+        public string DepartureDate { get; set; } // 离开日期
+    }
+
+    public class EmploymentInfo
+    {
+        ///<summary>现单位名称</summary>
+        public string CompanyName { get; set; }
+        ///<summary>现单位地址</summary>
+        public string CompanyAddress { get; set; }
+        ///<summary>单位电话</summary>
+        public string CompanyPhone { get; set; }
+        ///<summary>目前职位</summary>
+        public string Position { get; set; }
+        ///<summary>月收入</summary>
+        public decimal MonthlyIncome { get; set; }
+        ///<summary>赴新费用</summary>
+        public string TravelCostType { get; set; }
+        ///<summary>负责人姓名</summary>
+        public string ResponsiblePersonName { get; set; }
+        ///<summary>负责人电话</summary>
+        public string ResponsiblePersonPhone { get; set; }
+    }
+
+    public class FamilyMember
+    {
+        ///<summary>关系</summary>
+        public string Relation { get; set; }
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>婚姻状况</summary>
+        public string MaritalStatus { get; set; }
+        ///<summary>现国籍</summary>
+        public string CurrentNationality { get; set; }
+        ///<summary>出生国</summary>
+        public string BirthCountry { get; set; }
+        ///<summary>居住国</summary>
+        public string CesidenceCountry { get; set; }
+        ///<summary>现职务</summary>
+        public string CurrentPosition { get; set; }
+    }
+
+    public class Relation
+    {
+        ///<summary>关系类型</summary>
+        public string RelationType { get; set; }
+        ///<summary>姓名</summary>
+        public string Name { get; set; }
+        ///<summary>出生日期</summary>
+        public string BirthDate { get; set; }
+        ///<summary>地址</summary>
+        public string Address { get; set; }
+        ///<summary>联系方式</summary>
+        public string Contact { get; set; }
+    }
+
+    public class Rejection
+    {
+        ///<summary>国家</summary>
+        public string Country { get; set; }
+        ///<summary>申请日期</summary>
+        public string ApplicationDate { get; set; }
+        ///<summary>申请地点</summary>
+        public string ApplicationLocation { get; set; }
+        ///<summary>申请签证类别</summary>
+        public string VisaCategory { get; set; }
+        ///<summary>拒签原因</summary>
+        public string Reason { get; set; }
+    }
+
+    public class Education
+    {
+        ///<summary>院校名称</summary>
+        public string SchoolName { get; set; }
+        ///<summary>入学时间</summary>
+        public string EnrollmentDate { get; set; }
+        ///<summary>毕业时间</summary>
+        public string GraduationDate { get; set; }
+        ///<summary>课程专业名称</summary>
+        public string Major { get; set; }
+    }
+
+    public class WorkExperience
+    {
+        ///<summary>单位名称</summary>
+        public string CompanyName { get; set; }
+        ///<summary>开始时间</summary>
+        public string StartDate { get; set; }
+        ///<summary>结束时间</summary>
+        public string EndDate { get; set; }
+        ///<summary>单位所在地</summary>
+        public string Location { get; set; }
+        ///<summary>职务</summary>
+        public string Position { get; set; }
+    }
+
+    public class MilitaryService
+    {
+        ///<summary>军衔</summary>
+        public string Rank { get; set; }
+        ///<summary>所属部队</summary>
+        public string Unit { get; set; }
+        ///<summary>军种</summary>
+        public string Branch { get; set; }
+        ///<summary>服役时间</summary>
+        public string ServiceDate { get; set; }
+    }
+}

+ 1 - 3
OASystem/OASystem.Infrastructure/Repositories/Groups/VisaFeeInfoRepository.cs

@@ -534,8 +534,6 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 data = datas,
                 remark
             };
-
-
             return _result;
         }
 
@@ -594,4 +592,4 @@ namespace OASystem.Infrastructure.Repositories.Groups
             return (feeTotal, remark);
         }
     }
-}
+}

+ 2 - 6
OASystem/OASystem.Infrastructure/Repositories/PersonnelModule/GoodsRepository.cs

@@ -1,16 +1,12 @@
 using Aspose.Cells;
 using AutoMapper;
 using EyeSoft.Collections.Generic;
-using EyeSoft.Reflection;
 using Newtonsoft.Json;
 using OASystem.Domain;
 using OASystem.Domain.Dtos.PersonnelModule;
-using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.PersonnelModule;
-using OASystem.Domain.ViewModels.Financial;
 using OASystem.Domain.ViewModels.PersonnelModule;
-using OASystem.Infrastructure.Repositories.Financial;
 using OASystem.Infrastructure.Repositories.System;
 using OASystem.Infrastructure.Tools;
 
@@ -28,9 +24,9 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
         private string _excelPath;
         private List<int> _goodsTypeIds; //多部门审核物品类型Id     
         private readonly ApprovalProcessRepository _approvalProcessRep;
-        public GoodsRepository(SqlSugarClient sqlSugar, 
+        public GoodsRepository(SqlSugarClient sqlSugar,
             IMapper mapper,
-            ApprovalProcessRepository approvalProcessRep) 
+            ApprovalProcessRepository approvalProcessRep)
             : base(sqlSugar)
         {
             _mapper = mapper;