using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;
using FATrace.WPLApp.ModelDto;
using Prism.Mvvm;
namespace FATrace.WPLApp.Services
{
///
/// CSV服务
///导出CSV文件
///
public class CsvServices:BindableBase
{
public ILogService LogService { get; }
public IFreeSql FreeSql { get; }
public CsvServices(ILogService logService, IFreeSql freeSql)
{
LogService = logService;
FreeSql = freeSql;
}
///
/// 导出单条记录为 CSV 文件,文件名即记录的 InBagCode(自动追加 .csv 扩展名)。
///
/// 要导出的数据项,类型为 RawProUserCsvDto
/// 导出目录,不存在将自动创建
/// 若目标文件已存在,是否允许覆盖
/// 导出后的完整文件路径
/// item 或 outputDirectory 为 null
/// InBagCode 为空或仅空白
/// 文件已存在且不允许覆盖
public string ExportSingle(RawProUserCsvDto item, string outputDirectory, bool overwrite = false)
{
if (item is null) throw new ArgumentNullException(nameof(item));
if (outputDirectory is null) throw new ArgumentNullException(nameof(outputDirectory));
var inBagCode = (item.InBagCode ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(inBagCode))
throw new ArgumentException("InBagCode 不能为空", nameof(item.InBagCode));
// 目录确保存在
Directory.CreateDirectory(outputDirectory);
var safeName = SanitizeFileName(inBagCode);
var filePath = Path.Combine(outputDirectory, safeName + ".csv");
if (File.Exists(filePath) && !overwrite)
{
throw new IOException($"文件已存在且不允许覆盖: {filePath}");
}
// 使用 UTF8 BOM,便于 Excel 正确识别中文
using var writer = new StreamWriter(filePath, false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
// 固定列顺序映射,并设置日期格式
csv.Context.RegisterClassMap();
csv.WriteHeader();
csv.NextRecord();
csv.WriteRecord(item);
csv.NextRecord();
return filePath;
}
///
/// 批量导出:把每一条记录分别导出为以各自 InBagCode 命名的 CSV 文件。
///
/// 数据集合
/// 导出目录,不存在将自动创建
/// 若目标文件已存在,是否允许覆盖
/// 成功导出的文件完整路径集合
public IEnumerable ExportMany(IEnumerable items, string outputDirectory, bool overwrite = false)
{
if (items is null) throw new ArgumentNullException(nameof(items));
var results = new List();
foreach (var item in items)
{
// 逐条导出,沿用相同规则
var path = ExportSingle(item, outputDirectory, overwrite);
results.Add(path);
}
return results;
}
///
/// 清理文件名中的非法字符。
///
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);
}
return sb.ToString();
}
///
/// RawProUserCsvDto 的 CSV 列映射,固定输出顺序并设置日期格式。
///
private sealed class RawProUserCsvDtoMap : ClassMap
{
public RawProUserCsvDtoMap()
{
Map(x => x.RawCode).Index(0).Name(nameof(RawProUserCsvDto.RawCode));
Map(x => x.RawName).Index(1).Name(nameof(RawProUserCsvDto.RawName));
Map(x => x.InBagCode).Index(2).Name(nameof(RawProUserCsvDto.InBagCode));
Map(x => x.BoxCode).Index(3).Name(nameof(RawProUserCsvDto.BoxCode));
Map(x => x.Batch).Index(4).Name(nameof(RawProUserCsvDto.Batch));
Map(x => x.ShelfLife).Index(5).Name(nameof(RawProUserCsvDto.ShelfLife));
Map(x => x.Weight).Index(6).Name(nameof(RawProUserCsvDto.Weight));
Map(x => x.DeliveryDate).Index(7).Name(nameof(RawProUserCsvDto.DeliveryDate));
Map(x => x.RemainWeight).Index(8).Name(nameof(RawProUserCsvDto.RemainWeight));
Map(x => x.StockWeight).Index(9).Name(nameof(RawProUserCsvDto.StockWeight));
Map(x => x.WeightTime).Index(10).Name(nameof(RawProUserCsvDto.WeightTime)).TypeConverterOption.Format("yyyy-MM-dd HH:mm:ss");
Map(x => x.OpUser).Index(11).Name(nameof(RawProUserCsvDto.OpUser));
Map(x => x.CheckUser).Index(12).Name(nameof(RawProUserCsvDto.CheckUser));
Map(x => x.OutTime).Index(13).Name(nameof(RawProUserCsvDto.OutTime)).TypeConverterOption.Format("yyyy-MM-dd HH:mm:ss");
}
}
}
}