647556386
3 天以前 2f8fc989f339a936b01092caebd4c46e6109da1b
WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -79,6 +79,7 @@
using WIDESEA_InboundRepository;
using System.Drawing.Printing;
using System;
using WIDESEA_Common.Log;
namespace WIDESEA_TaskInfoService
{
@@ -107,7 +108,8 @@
        private readonly IStockInfoDetailRepository _stockInfoDetailRepository;
        private readonly IReturnOrderRepository _returnOrderRepository;
        private readonly IProductionRepository _productionRepository;
        private readonly IInboundRepository _inboundRepository;
        private readonly IInboundRepository _inboundRepository;
        public ITaskRepository Repository => BaseDal;
        public TaskService(ITaskRepository BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IMaterielInfoService materielInfoService, IInboundOrderDetail_HtyService inboundOrderDetail_HtyService, IOutboundOrder_HtyService outboundOrder_HtyService, IOutboundOrderDetail_HtyService outboundOrderDetail_HtyService, IInboundOrder_HtyService inboundOrder_HtyService, IStockRepository stockRepository, IInboundOrderDetailService inboundOrderDetailService, IBasicService basicService, IOutboundService outboundService, IInboundService inboundService, IRecordService recordService, IStockService stockService, ITask_HtyService taskHtyService, ILocationInfoService locationInfoService, IOutboundOrderDetailRepository outboundOrderDetailRepository, IBasicRepository basicRepository, IStockInfoDetailRepository stockInfoDetailRepository, IPalletTypeInfoRepository palletTypeInfoRepository, IReturnOrderRepository returnOrderRepository, IProductionRepository productionRepository,IInboundRepository inboundRepository) : base(BaseDal)
@@ -144,6 +146,7 @@
        public string ReceiveWMSTaskAllocatein = WIDESEA_Core.Helper.AppSettings.Configuration["ReceiveWMSTaskAllocatein"];
        public string ReceiveERPTaskout = WIDESEA_Core.Helper.AppSettings.Configuration["ReceiveERPTaskout"];
        public string InMaterialWarehousingCallback = WIDESEA_Core.Helper.AppSettings.Configuration["InMaterialWarehousingCallback"];
        /// <summary>
        /// 任务信息推送至WCS
@@ -187,7 +190,7 @@
                {
                    return WebResponseContent.Instance.Error($"未找到WCSApi地址,请检查配置文件");
                }
                string response = HttpHelper.Get($"{url}/api/Task/RecWMSTaskCompleted?taskNum=" + taskNum);
                string response = HttpHelper.Post($"{url}/api/Task/RecWMSTaskCompleted?taskNum=" + taskNum);
                return JsonConvert.DeserializeObject<WebResponseContent>(response) ?? WebResponseContent.Instance.Error("返回错误");
            }
@@ -1205,8 +1208,7 @@
                if (outboundOrderDetails.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt() && inboundOrder.System.Equals("ERP"))
                {
                    FeedBackOutERP(outboundOrder.OrderNo, outboundOrderDetails.LinId);
                    //DownloadReport(path, savePath, outboundOrderDetails.Id);
                    //printTest(savePath);
                    DownloadReport(path, savePath, outboundOrderDetails.Id);
                }
                ///单据完成推送SMOM系统
                if (outboundOrder.OrderStatus == OutboundStatusEnum.出库完成.ObjToInt() && inboundOrder.System.Equals("SMOM"))
@@ -1664,72 +1666,348 @@
                throw new Exception($"操作失败: {response.Message ?? "未提供错误信息"}");
            }
        }
        public string DownloadReport(string path, string savePath, int orderId)
        // 方法声明添加 async 关键字,返回值改为 Task<string>
        public async Task<string> DownloadReport(string path, string savePath, int orderId)
        {
            // 构建完整 URL,(报表传参)
            string reportUrl = path + "&orderId=" + orderId;
            try
            {
                // 确保保存目录存在
                string directory = Path.GetDirectoryName(savePath);
                Console.WriteLine($"下载地址: {reportUrl}");
                Console.WriteLine($"保存路径: {savePath}");
                // 确保目录存在
                if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                    Console.WriteLine($"已创建目录: {directory}");
                }
                string[] files = Directory.GetFiles(directory);
                // 删除每个文件
                foreach (string filePath in files)
                // 清理目录下现有文件
                if (File.Exists(savePath))
                {
                    File.Delete(filePath);
                    File.Delete(savePath);
                    Console.WriteLine($"已删除旧文件: {savePath}");
                }
                // 使用 HttpClient 下载文件
                // 使用临时文件名下载,下载完成后再重命名
                string tempFilePath = savePath + ".tmp";
                // 异步下载文件
                using (var httpClient = new HttpClient())
                {
                    // 设置超时时间(帆软报表生成可能需要较长时间)
                    httpClient.Timeout = TimeSpan.FromMinutes(5);
                    // 发送 GET 请求(同步方式)
                    using (var response = httpClient.GetAsync(reportUrl, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult())
                    using (var response = await httpClient.GetAsync(reportUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
                    {
                        // 检查响应状态
                        response.EnsureSuccessStatusCode();
                        // 获取内容流(同步方式)
                        using (var contentStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
                        using (var contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            // 创建文件流
                            using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
                            Console.WriteLine($"开始写入临时文件: {tempFilePath}");
                            using (var fileStream = new FileStream(
                                tempFilePath,
                                FileMode.Create,
                                FileAccess.Write,
                                FileShare.None,
                                bufferSize: 4096,
                                useAsync: true
                            ))
                            {
                                // 复制内容到文件(同步方式)
                                contentStream.CopyToAsync(fileStream).GetAwaiter().GetResult();
                                await contentStream.CopyToAsync(fileStream).ConfigureAwait(false);
                                await fileStream.FlushAsync().ConfigureAwait(false);
                                Console.WriteLine($"临时文件写入完成: {tempFilePath}");
                            }
                        }
                    }
                }
                return savePath;
                // 关键优化:确保文件完全写入磁盘
                await EnsureFileCompletelyWritten(tempFilePath);
                // 重命名为目标文件
                File.Move(tempFilePath, savePath, true);
                Console.WriteLine($"文件重命名完成: {savePath}");
                // 验证文件可访问
                await ValidateFileAccessible(savePath);
                // 打印文件
                PrintTestDirect(savePath);
                return savePath;
            }
            catch (Exception ex)
            {
                throw new ValidationException(ex.Message);
                Console.WriteLine($"下载失败: {ex.Message}");
                throw new InvalidOperationException($"文件下载异常: {ex.Message}", ex);
            }
        }
        public virtual void printTest(string fullPath)
        /// <summary>
        /// 确保文件完全写入磁盘
        /// </summary>
        private async Task EnsureFileCompletelyWritten(string filePath)
        {
            Spire.Pdf.PdfDocument pdf = new PdfDocument();
            //文件地址
            pdf.LoadFromFile(fullPath);
            //指定打印机位置
            string url = AppSettings.app("PrinterName");
            pdf.PrintSettings.PrinterName = url;
            //执行打印
            pdf.Print();
            //内存释放
            pdf.Dispose();
            const int maxRetries = 5;
            const int delayMs = 500;
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    // 尝试以读取模式打开文件,确保文件没有被占用
                    using (var fs = new FileStream(filePath,
                        FileMode.Open,
                        FileAccess.Read,
                        FileShare.Read,
                        bufferSize: 4096,
                        useAsync: true))
                    {
                        // 验证文件有内容
                        if (fs.Length > 0)
                        {
                            Console.WriteLine($"文件验证成功: 大小={fs.Length}字节 (尝试 {i + 1}/{maxRetries})");
                            return;
                        }
                        else
                        {
                            Console.WriteLine($"警告: 文件大小为0 (尝试 {i + 1}/{maxRetries})");
                        }
                    }
                }
                catch (IOException ex)
                {
                    Console.WriteLine($"文件可能仍在写入中 (尝试 {i + 1}/{maxRetries}): {ex.Message}");
                }
                if (i < maxRetries - 1)
                {
                    await Task.Delay(delayMs);
                }
            }
            throw new IOException($"文件写入未完成或无法访问: {filePath}");
        }
        /// <summary>
        /// 验证文件可访问且不为空
        /// </summary>
        private async Task ValidateFileAccessible(string filePath)
        {
            const int maxRetries = 3;
            const int delayMs = 300;
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    if (!File.Exists(filePath))
                    {
                        throw new FileNotFoundException($"文件不存在: {filePath}");
                    }
                    var fileInfo = new FileInfo(filePath);
                    // 验证文件大小大于0
                    if (fileInfo.Length == 0)
                    {
                        throw new InvalidDataException($"文件大小为0: {filePath}");
                    }
                    // 验证文件不是完全由空字符组成(可选检查)
                    await ValidateFileContent(filePath);
                    Console.WriteLine($"文件验证通过: {filePath} (大小={fileInfo.Length}字节)");
                    return;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"文件验证失败 (尝试 {i + 1}/{maxRetries}): {ex.Message}");
                    if (i == maxRetries - 1)
                    {
                        throw;
                    }
                    await Task.Delay(delayMs);
                }
            }
        }
        /// <summary>
        /// 验证文件内容(可选)
        /// </summary>
        private async Task ValidateFileContent(string filePath)
        {
            try
            {
                // 读取文件前几个字节,确保不是完全由空字符组成
                using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
                {
                    byte[] buffer = new byte[Math.Min(1024, fs.Length)];
                    int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
                    // 检查是否都是0(空文件)
                    if (bytesRead > 0 && buffer.All(b => b == 0))
                    {
                        throw new InvalidDataException("文件内容异常: 全部为0字节");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"文件内容验证异常: {ex.Message}");
                // 可以选择是否抛出异常
            }
        }
        public virtual void PrintTestDirect(string fullPath)
        {
            const int maxRetryCount = 3;
            const int retryDelayMs = 1000;
            if (!File.Exists(fullPath))
            {
                Console.WriteLine($"打印失败:文件不存在 {fullPath}");
                return;
            }
            // 获取打印机名称
            string printerName = AppSettings.app("PrinterName");
            // 尝试不同的打印方法
            for (int retryCount = 0; retryCount < maxRetryCount; retryCount++)
            {
                try
                {
                    Console.WriteLine($"尝试打印 (方法 {retryCount + 1}/{maxRetryCount}): {fullPath}");
                    switch (retryCount)
                    {
                        case 0:
                            // 方法1: 使用原始打印命令
                            PrintUsingRawCommand(fullPath, printerName);
                            break;
                        case 1:
                            // 方法3: 直接使用Spire.PDF但简化设置
                            PrintUsingSpireSimple(fullPath, printerName);
                            break;
                        case 2:
                            // 方法3: 直接使用Spire.PDF但简化设置
                            PrintUsingSpireSimple(fullPath, printerName);
                            break;
                    }
                    Console.WriteLine("打印任务发送成功");
                    return;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"打印方法 {retryCount + 1} 失败: {ex.Message}");
                    if (retryCount < maxRetryCount - 1)
                    {
                        Thread.Sleep(retryDelayMs);
                    }
                }
            }
            Console.WriteLine("所有打印方法都失败");
        }
        /// <summary>
        /// 使用原始打印命令
        /// </summary>
        private void PrintUsingRawCommand(string filePath, string printerName)
        {
            try
            {
                // 方法1: 使用Process直接打印
                var process = new System.Diagnostics.Process();
                process.StartInfo.FileName = filePath;
                process.StartInfo.Verb = "print";
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                process.Start();
                // 等待一段时间让打印任务提交
                if (!process.WaitForExit(5000))
                {
                    Console.WriteLine("打印进程未及时退出,但任务可能已提交");
                }
            }
            catch
            {
                // 如果上述方法失败,尝试使用rundll32
                try
                {
                    var args = $@"/c rundll32.exe C:\Windows\System32\shimgvw.dll,ImageView_PrintTo ""{filePath}"" ""{printerName}""";
                    System.Diagnostics.Process.Start("cmd.exe", args);
                }
                catch
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// 使用Spire.PDF但简化设置
        /// </summary>
        private void PrintUsingSpireSimple(string filePath, string printerName)
        {
            using (Spire.Pdf.PdfDocument pdf = new Spire.Pdf.PdfDocument())
            {
                // 使用最小配置加载文件
                pdf.LoadFromFile(filePath);
                // 设置基本打印机信息
                if (!string.IsNullOrEmpty(printerName))
                {
                    pdf.PrintSettings.PrinterName = printerName;
                }
                // 直接打印,不检查
                pdf.Print();
            }
        }
        /// <summary>
        /// 检查文件是否可访问
        /// </summary>
        private bool IsFileAccessible(string filePath)
        {
            try
            {
                using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    return fs.Length > 0;
                }
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 尝试强制垃圾回收,释放可能存在的文件句柄
        /// </summary>
        private void TryForceGarbageCollection()
        {
            try
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
                Console.WriteLine("已执行垃圾回收");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"垃圾回收失败: {ex.Message}");
            }
        }