TH1/ExcelExport/Export.cs
2025-12-02 16:31:52 +08:00

511 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: 白哉
* @Description:
* @Date: 2025年12月02日 星期二 15:12:27
* @Modify:
*/
using System.Text;
using OfficeOpenXml;
namespace ExcelConfig
{
public class HeadInfo
{
public string FieldDesc { get; set; }
public string FieldName { get; set; }
public string FieldType { get; set; }
public int FieldIndex { get; set; }
public HeadInfo(string desc, string name, string type, int index)
{
this.FieldDesc = desc;
this.FieldName = name;
this.FieldType = type;
this.FieldIndex = index;
}
}
public class Table
{
public int Index { get; set; }
public Dictionary<string, HeadInfo?> HeadInfos { get; set; } = new Dictionary<string, HeadInfo?>();
}
public static class ExcelExporter
{
private static string templateMain = "";
private static string templatePartial = "";
private const string ClassDir = "GenerateCS";
private const string TargetClassDir = "../Unity/Assets/Scripts/TH1_Config/GenerateCS";
private const string ClassPartialDir = "GenerateCSPartial";
private const string excelDir = "./Excel/";
private const string BytesDir = "GenerateBytes";
private const string TargetBytesDir = "../Unity/Assets/Resources/ExcelConfig/GenerateBytes";
private static Dictionary<string, Table> tables = new Dictionary<string, Table>();
private static Dictionary<string, ExcelPackage> packages = new Dictionary<string, ExcelPackage>();
private static Table GetTable(string protoName)
{
if (!tables.TryGetValue(protoName, out var table))
{
table = new Table();
tables[protoName] = table;
}
return table;
}
public static ExcelPackage GetPackage(string filePath)
{
if (!packages.TryGetValue(filePath, out var package))
{
using Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
package = new ExcelPackage(stream);
packages[filePath] = package;
}
return package;
}
// 开始导CS
public static void ExportCS()
{
try
{
templateMain = File.ReadAllText("ExportTemplate_Main.txt");
templatePartial = File.ReadAllText("ExportTemplate_Partial.txt");
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
// 清理旧的生成目录
CleanDirectory(ClassDir);
CleanDirectory(ClassPartialDir);
// 扫描所有Excel文件收集表头信息
List<string> files = FileHelper.GetAllFiles(excelDir, "*.xlsx");
foreach (string path in files)
{
string fileName = Path.GetFileName(path);
if (!fileName.EndsWith(".xlsx") || fileName.StartsWith("~$") || fileName.Contains("#"))
{
continue;
}
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
ExcelPackage p = GetPackage(Path.GetFullPath(path));
string protoName = fileNameWithoutExtension;
if (fileNameWithoutExtension.Contains('_'))
{
protoName = fileNameWithoutExtension.Substring(0, fileNameWithoutExtension.LastIndexOf('_'));
}
Table table = GetTable(protoName);
ExportExcelClass(p, protoName, table);
}
// 第二步生成C#类文件
foreach (var kv in tables)
{
ExportClass(kv.Key, kv.Value.HeadInfos);
}
Console.WriteLine("ExcelCS 导出成功!");
Console.WriteLine($"生成的CS文件位于: {Path.GetFullPath(ClassDir)}");
}
catch (Exception e)
{
Console.WriteLine($"导出失败: {e}");
throw;
}
finally
{
tables.Clear();
foreach (var kv in packages)
{
kv.Value.Dispose();
}
packages.Clear();
}
}
// 开始导Bytes
public static void ExportBytes()
{
try
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
// 清理旧的生成目录
CleanDirectory(BytesDir);
// 扫描所有Excel文件收集表头信息
List<string> files = FileHelper.GetAllFiles(excelDir, "*.xlsx");
foreach (string path in files)
{
string fileName = Path.GetFileName(path);
if (!fileName.EndsWith(".xlsx") || fileName.StartsWith("~$") || fileName.Contains("#"))
{
continue;
}
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
ExcelPackage p = GetPackage(Path.GetFullPath(path));
string protoName = fileNameWithoutExtension;
if (fileNameWithoutExtension.Contains('_'))
{
protoName = fileNameWithoutExtension.Substring(0, fileNameWithoutExtension.LastIndexOf('_'));
}
ExportClassBytes(protoName, p);
}
Console.WriteLine("ExcelBytes 导出成功!");
Console.WriteLine($"生成的二进制文件位于: {Path.GetFullPath(BytesDir)}");
}
catch (Exception e)
{
Console.WriteLine($"导出失败: {e}");
throw;
}
finally
{
tables.Clear();
foreach (var kv in packages)
{
kv.Value.Dispose();
}
packages.Clear();
}
}
// 文件移动
public static void MoveFile()
{
try
{
// 清空并拷贝CS文件
if (Directory.Exists(ClassDir))
{
CleanDirectory(TargetClassDir);
CopyDirectory(ClassDir, TargetClassDir);
Console.WriteLine($"成功拷贝CS文件: {ClassDir} -> {TargetClassDir}");
}
else
{
Console.WriteLine($"警告: 源目录不存在: {ClassDir}");
}
// 清空并拷贝Bytes文件
if (Directory.Exists(BytesDir))
{
CleanDirectory(TargetBytesDir);
CopyDirectory(BytesDir, TargetBytesDir);
Console.WriteLine($"成功拷贝Bytes文件: {BytesDir} -> {TargetBytesDir}");
}
else
{
Console.WriteLine($"警告: 源目录不存在: {BytesDir}");
}
Console.WriteLine("文件移动完成!");
}
catch (Exception e)
{
Console.WriteLine($"文件移动失败: {e}");
throw;
}
}
private static void CopyDirectory(string sourceDir, string targetDir)
{
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}
// 拷贝所有文件
string[] files = Directory.GetFiles(sourceDir);
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
string targetFile = Path.Combine(targetDir, fileName);
File.Copy(file, targetFile, true);
}
// 递归拷贝子目录
string[] subDirs = Directory.GetDirectories(sourceDir);
foreach (string subDir in subDirs)
{
string dirName = Path.GetFileName(subDir);
string targetSubDir = Path.Combine(targetDir, dirName);
CopyDirectory(subDir, targetSubDir);
}
}
private static void CleanDirectory(string dir)
{
if (Directory.Exists(dir))
{
Directory.Delete(dir, true);
}
Directory.CreateDirectory(dir);
}
#region class
static void ExportExcelClass(ExcelPackage p, string name, Table table)
{
ExportSheetClass(p.Workbook.Worksheets.First(), table);
}
static void ExportSheetClass(ExcelWorksheet worksheet, Table table)
{
const int row = 2;
if (worksheet.Name.StartsWith("#"))
{
return;
}
for (int col = 3; col <= worksheet.Dimension.End.Column; ++col)
{
string fieldName = worksheet.Cells[row + 2, col].Text.Trim();
if (string.IsNullOrEmpty(fieldName))
{
continue;
}
if (table.HeadInfos.ContainsKey(fieldName))
{
continue;
}
string fieldCheck = worksheet.Cells[row, col].Text.Trim();
if (fieldCheck.Contains("#"))
{
table.HeadInfos[fieldName] = null;
continue;
}
string fieldDesc = worksheet.Cells[row + 1, col].Text.Trim();
string fieldType = worksheet.Cells[row + 3, col].Text.Trim();
table.HeadInfos[fieldName] = new HeadInfo(fieldDesc, fieldName, fieldType, ++table.Index);
}
}
static void ExportClass(string protoName, Dictionary<string, HeadInfo?> classField)
{
if (!Directory.Exists(ClassDir))
{
Directory.CreateDirectory(ClassDir);
}
if (!Directory.Exists(ClassPartialDir))
{
Directory.CreateDirectory(ClassPartialDir);
}
StringBuilder sbFields = new StringBuilder();
StringBuilder sbConstructor = new StringBuilder();
foreach ((string _, HeadInfo? headInfo) in classField)
{
if (headInfo == null)
{
continue;
}
// 生成字段
sbFields.Append($"\t\t/// <summary>{headInfo.FieldDesc}</summary>\n");
sbFields.Append($"\t\t[MemoryPackInclude]\n");
string fieldType = headInfo.FieldType;
sbFields.Append($"\t\tpublic {fieldType} {headInfo.FieldName} {{ get; set; }}\n");
// 生成构造函数体
int colIndex = headInfo.FieldIndex + 2; // 列索引从3开始FieldIndex从1开始所以+2
sbConstructor.Append($"\t\t\t{headInfo.FieldName} = ({fieldType})ExcelExporter.ConvertValue(cells[row, {colIndex}].Text.Trim(), typeof({fieldType}));\n");
}
// 生成主文件 (GenerateCS目录)
string mainExportPath = Path.Combine(ClassDir, $"{protoName}.cs");
using (FileStream txt = new FileStream(mainExportPath, FileMode.Create))
using (StreamWriter sw = new StreamWriter(txt))
{
string mainContent = templateMain
.Replace("(ConfigName)", protoName)
.Replace("(Fields)", sbFields.ToString());
sw.Write(mainContent);
}
// 生成Partial文件 (GenerateCSPartial目录)
string partialExportPath = Path.Combine(ClassPartialDir, $"{protoName}.cs");
using (FileStream txt = new FileStream(partialExportPath, FileMode.Create))
using (StreamWriter sw = new StreamWriter(txt))
{
string partialContent = templatePartial
.Replace("(ConfigName)", protoName)
.Replace("(ConstructorBody)", sbConstructor.ToString());
sw.Write(partialContent);
}
}
static void ExportClassBytes(string name, ExcelPackage p)
{
try
{
// 1. 构造完整类型名 (包含命名空间)
string categoryTypeName = $"ExcelConfig.{name}Category";
// 2. 从所有已加载的程序集中查找类型
Type? categoryType = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(t => t.FullName == categoryTypeName);
if (categoryType == null)
{
Console.WriteLine($"警告: 未找到类型 {categoryTypeName},跳过序列化");
return;
}
// 3. 创建实例并序列化
object? categoryInstance = Activator.CreateInstance(categoryType);
if (categoryInstance is ExcelConfigBase excelConfig)
{
excelConfig.Serialization(p);
// 4. 写入文件
string exportPath = Path.Combine(BytesDir, $"{name}.bytes");
File.WriteAllBytes(exportPath, excelConfig.GetData());
Console.WriteLine($"成功生成: {exportPath}");
}
else
{
Console.WriteLine($"警告: {categoryTypeName} 未实现 IExcelConfig 接口");
}
}
catch (Exception ex)
{
Console.WriteLine($"导出 {name} 失败: {ex.Message}");
throw;
}
}
public static object ConvertValue(string value, Type targetType)
{
value = value?.Trim() ?? string.Empty;
// 处理数组类型
if (targetType.IsArray)
{
return ConvertArrayValue(value, targetType);
}
// 处理基础类型
return ConvertSingleValue(value, targetType);
}
private static object ConvertArrayValue(string value, Type arrayType)
{
Type? elementType = arrayType.GetElementType();
if (elementType == null)
{
throw new InvalidOperationException("无法获取数组的元素类型");
}
if (string.IsNullOrWhiteSpace(value))
{
return Array.CreateInstance(elementType, 0);
}
string[] parts = value.Split([','], StringSplitOptions.RemoveEmptyEntries);
Array array = Array.CreateInstance(elementType, parts.Length);
for (int i = 0; i < parts.Length; i++)
{
array.SetValue(ConvertSingleValue(parts[i].Trim(), elementType), i);
}
return array;
}
private static object ConvertSingleValue(string value, Type targetType)
{
if (string.IsNullOrWhiteSpace(value))
{
if (targetType == typeof(string))
return string.Empty;
if (targetType == typeof(bool))
return false;
return 0; // 数值类型默认返回0
}
try
{
if (targetType == typeof(int))
return int.Parse(value);
if (targetType == typeof(uint))
return uint.Parse(value);
if (targetType == typeof(long))
return long.Parse(value);
if (targetType == typeof(float))
return float.Parse(value);
if (targetType == typeof(double))
return double.Parse(value);
if (targetType == typeof(bool))
return bool.Parse(value.ToLower());
if (targetType == typeof(string))
return value;
throw new NotSupportedException($"不支持的类型: {targetType.Name}");
}
catch (FormatException ex)
{
throw new FormatException($"无法将 '{value}' 转换为 {targetType.Name} 类型", ex);
}
}
#endregion
}
public static class FileHelper
{
public static List<string> GetAllFiles(string dir, string searchPattern = "*")
{
List<string> list = new List<string>();
GetAllFiles(list, dir, searchPattern);
return list;
}
public static void GetAllFiles(List<string> files, string dir, string searchPattern = "*")
{
if (!Directory.Exists(dir))
{
return;
}
string[] fls = Directory.GetFiles(dir, searchPattern);
foreach (string fl in fls)
{
files.Add(fl);
}
string[] subDirs = Directory.GetDirectories(dir);
foreach (string subDir in subDirs)
{
GetAllFiles(files, subDir, searchPattern);
}
}
}
}