using CsvHelper; using CsvHelper.Configuration; using FATrace.Com; using FATrace.OEMApp.Model; using NLog; using System.Globalization; using System.Text; namespace FATrace.OEMApp.Services { public class CsvService { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); public CsvService() { RawUseCsvPath = ConfigHelper.GetValue("RawUseCsvPath"); } /// /// 原料使用信息CSV文件路径 /// public string RawUseCsvPath { get; set; } /// /// 将一条原料使用记录导出为单独CSV文件(包含表头) /// 文件保存目录来自 RawUseCsvPath;文件名包含时间戳与内袋二维码(若有) /// /// 原料使用记录 /// 生成的CSV完整路径 /// 当 data 为空时抛出 /// 当 RawUseCsvPath 未配置时抛出 /// 当写入文件失败时抛出 public string ExportSingle(RawUseCsvDto data) { if (data == null) throw new ArgumentNullException(nameof(data)); if (string.IsNullOrWhiteSpace(RawUseCsvPath)) { const string msg = "RawUseCsvPath 未配置,无法导出CSV。"; _logger.Error(msg); throw new InvalidOperationException(msg); } try { // 确保目录存在 Directory.CreateDirectory(RawUseCsvPath); // 构建安全文件名:时间戳_内袋二维码.csv(若二维码为空则用RawUse代替) var safeCode = SanitizeFileName(string.IsNullOrWhiteSpace(data.InBagCode) ? "RawUse" : data.InBagCode!.Trim()); var fileName = $"{safeCode}.csv"; var fullPath = Path.Combine(RawUseCsvPath, fileName); var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true, }; using (var writer = new StreamWriter(fullPath, false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true))) using (var csv = new CsvWriter(writer, config)) { csv.Context.RegisterClassMap(); csv.WriteHeader(); csv.NextRecord(); csv.WriteRecord(data); csv.NextRecord(); } _logger.Info($"CSV 导出成功: {fullPath}"); return fullPath; } catch (Exception ex) { _logger.Error(ex, "导出原料使用CSV失败。"); throw new IOException("导出原料使用CSV失败。", ex); } } private static string SanitizeFileName(string name) { var invalid = Path.GetInvalidFileNameChars(); var sb = new StringBuilder(name.Length); foreach (var ch in name) { sb.Append(invalid.Contains(ch) ? '_' : ch); } var result = sb.ToString(); return string.IsNullOrWhiteSpace(result) ? "RawUse" : result; } } }