Program.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Text;
  7. using Microsoft.CodeAnalysis;
  8. using Microsoft.CodeAnalysis.CSharp;
  9. using Microsoft.CodeAnalysis.Emit;
  10. using MongoDB.Bson.Serialization;
  11. using MongoDB.Bson.Serialization.Attributes;
  12. using OfficeOpenXml;
  13. using ProtoBuf;
  14. using LicenseContext = OfficeOpenXml.LicenseContext;
  15. namespace ET
  16. {
  17. public enum ConfigType
  18. {
  19. Client,
  20. Server,
  21. }
  22. struct HeadInfo
  23. {
  24. public string FieldAttribute;
  25. public string FieldDesc;
  26. public string FieldName;
  27. public string FieldType;
  28. public HeadInfo(string cs, string desc, string name, string type)
  29. {
  30. this.FieldAttribute = cs;
  31. this.FieldDesc = desc;
  32. this.FieldName = name;
  33. this.FieldType = type;
  34. }
  35. }
  36. class Program
  37. {
  38. private static string template;
  39. private const string clientClassDir = "../../../Unity/Assets/Model/Generate/Config";
  40. private const string serverClassDir = "../../../Server/Model/Generate/Config";
  41. private const string excelDir = "../../../Excel";
  42. private const string jsonDir = "./{0}/Json";
  43. private const string clientProtoDir = "../../../Unity/Assets/Bundles/Config";
  44. private const string serverProtoDir = "../../../Config";
  45. private static string GetProtoDir(ConfigType configType)
  46. {
  47. if (configType == ConfigType.Client)
  48. {
  49. return clientProtoDir;
  50. }
  51. return serverProtoDir;
  52. }
  53. private static string GetClassDir(ConfigType configType)
  54. {
  55. if (configType == ConfigType.Client)
  56. {
  57. return clientClassDir;
  58. }
  59. return serverClassDir;
  60. }
  61. static void Main(string[] args)
  62. {
  63. try
  64. {
  65. template = File.ReadAllText("Template.txt");
  66. ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
  67. foreach (string path in Directory.GetFiles(excelDir, "*.xlsx"))
  68. {
  69. using Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  70. using ExcelPackage p = new ExcelPackage(stream);
  71. string name = Path.GetFileNameWithoutExtension(path);
  72. ExportExcelClass(p, name, ConfigType.Client);
  73. ExportExcelClass(p, name, ConfigType.Server);
  74. ExportExcelJson(p, name, ConfigType.Client);
  75. ExportExcelJson(p, name, ConfigType.Server);
  76. }
  77. ExportExcelProtobuf(ConfigType.Client);
  78. ExportExcelProtobuf(ConfigType.Server);
  79. Console.WriteLine("导表成功!");
  80. }
  81. catch (Exception e)
  82. {
  83. Console.WriteLine(e);
  84. }
  85. }
  86. #region 导出class
  87. static void ExportExcelClass(ExcelPackage p, string name, ConfigType configType)
  88. {
  89. List<HeadInfo> classField = new List<HeadInfo>();
  90. HashSet<string> uniqeField = new HashSet<string>();
  91. foreach (ExcelWorksheet worksheet in p.Workbook.Worksheets)
  92. {
  93. ExportSheetClass(worksheet, classField, uniqeField, configType);
  94. }
  95. ExportClass(name, classField, configType);
  96. }
  97. static void ExportSheetClass(ExcelWorksheet worksheet, List<HeadInfo> classField, HashSet<string> uniqeField, ConfigType configType)
  98. {
  99. const int row = 2;
  100. for (int col = 3; col <= worksheet.Dimension.End.Column; ++col)
  101. {
  102. string fieldName = worksheet.Cells[row + 2, col].Text.Trim();
  103. if (fieldName == "")
  104. {
  105. continue;
  106. }
  107. if (!uniqeField.Add(fieldName))
  108. {
  109. continue;
  110. }
  111. string fieldCS = worksheet.Cells[row, col].Text.Trim();
  112. string fieldDesc = worksheet.Cells[row + 1, col].Text.Trim();
  113. string fieldType = worksheet.Cells[row + 3, col].Text.Trim();
  114. classField.Add(new HeadInfo(fieldCS, fieldDesc, fieldName, fieldType));
  115. }
  116. }
  117. static void ExportClass(string protoName, List<HeadInfo> classField, ConfigType configType)
  118. {
  119. string dir = GetClassDir(configType);
  120. if (!Directory.Exists(dir))
  121. {
  122. Directory.CreateDirectory(dir);
  123. }
  124. string exportPath = Path.Combine(dir, $"{protoName}.cs");
  125. using FileStream txt = new FileStream(exportPath, FileMode.Create);
  126. using StreamWriter sw = new StreamWriter(txt);
  127. StringBuilder sb = new StringBuilder();
  128. for (int i = 0; i < classField.Count; i++)
  129. {
  130. HeadInfo headInfo = classField[i];
  131. if (headInfo.FieldAttribute.StartsWith("#"))
  132. {
  133. continue;
  134. }
  135. sb.Append($"\t\t[ProtoMember({i + 1}, IsRequired = true)]\n");
  136. sb.Append($"\t\tpublic {headInfo.FieldType} {headInfo.FieldName} {{ get; set; }}\n");
  137. }
  138. string content = template.Replace("(ConfigName)", protoName).Replace(("(Fields)"), sb.ToString());
  139. sw.Write(content);
  140. }
  141. #endregion
  142. #region 导出json
  143. static void ExportExcelJson(ExcelPackage p, string name, ConfigType configType)
  144. {
  145. StringBuilder sb = new StringBuilder();
  146. sb.AppendLine("{\"list\":[");
  147. foreach (ExcelWorksheet worksheet in p.Workbook.Worksheets)
  148. {
  149. ExportSheetJson(worksheet, configType, sb);
  150. }
  151. sb.AppendLine("]}");
  152. string dir = string.Format(jsonDir, configType.ToString());
  153. if (!Directory.Exists(dir))
  154. {
  155. Directory.CreateDirectory(dir);
  156. }
  157. string jsonPath = Path.Combine(dir, $"{name}.txt");
  158. using FileStream txt = new FileStream(jsonPath, FileMode.Create);
  159. using StreamWriter sw = new StreamWriter(txt);
  160. sw.Write(sb.ToString());
  161. }
  162. static void ExportSheetJson(ExcelWorksheet worksheet, ConfigType configType, StringBuilder sb)
  163. {
  164. int infoRow = 2;
  165. HeadInfo[] headInfos = new HeadInfo[100];
  166. for (int col = 3; col <= worksheet.Dimension.End.Column; ++col)
  167. {
  168. string fieldCS = worksheet.Cells[infoRow, col].Text.Trim();
  169. if (fieldCS.Contains("#"))
  170. {
  171. continue;
  172. }
  173. string fieldName = worksheet.Cells[infoRow + 2, col].Text.Trim();
  174. if (fieldName == "")
  175. {
  176. continue;
  177. }
  178. string fieldDesc = worksheet.Cells[infoRow + 1, col].Text.Trim();
  179. string fieldType = worksheet.Cells[infoRow + 3, col].Text.Trim();
  180. headInfos[col] = new HeadInfo(fieldCS, fieldDesc, fieldName, fieldType);
  181. }
  182. for (int row = 6; row <= worksheet.Dimension.End.Row; ++row)
  183. {
  184. sb.Append("{");
  185. for (int col = 3; col < worksheet.Dimension.End.Column; ++col)
  186. {
  187. HeadInfo headInfo = headInfos[col];
  188. if (headInfo.FieldAttribute == null)
  189. {
  190. continue;
  191. }
  192. if (headInfo.FieldAttribute.Contains("#"))
  193. {
  194. continue;
  195. }
  196. if (headInfo.FieldName == "Id")
  197. {
  198. headInfo.FieldName = "_id";
  199. }
  200. else
  201. {
  202. sb.Append(",");
  203. }
  204. sb.Append($"\"{headInfo.FieldName}\":{Convert(headInfo.FieldType, worksheet.Cells[row, col].Text.Trim())}");
  205. }
  206. sb.Append("},\n");
  207. }
  208. }
  209. private static string Convert(string type, string value)
  210. {
  211. switch (type)
  212. {
  213. case "int[]":
  214. case "int32[]":
  215. case "long[]":
  216. return $"[{value}]";
  217. case "string[]":
  218. return $"[{value}]";
  219. case "int":
  220. case "int32":
  221. case "int64":
  222. case "long":
  223. case "float":
  224. case "double":
  225. return value;
  226. case "string":
  227. return $"\"{value}\"";
  228. default:
  229. throw new Exception($"不支持此类型: {type}");
  230. }
  231. }
  232. #endregion
  233. // 根据生成的类,动态编译把json转成protobuf
  234. private static void ExportExcelProtobuf(ConfigType configType)
  235. {
  236. string classPath = GetClassDir(configType);
  237. List<SyntaxTree> syntaxTrees = new List<SyntaxTree>();
  238. List<string> protoNames = new List<string>();
  239. foreach (string classFile in Directory.GetFiles(classPath, "*.cs"))
  240. {
  241. protoNames.Add(Path.GetFileNameWithoutExtension(classFile));
  242. syntaxTrees.Add(CSharpSyntaxTree.ParseText(File.ReadAllText(classFile)));
  243. }
  244. List<PortableExecutableReference> references = new List<PortableExecutableReference>();
  245. string assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
  246. references.Add(AssemblyMetadata.CreateFromFile(typeof(object).Assembly.Location).GetReference());
  247. references.Add(AssemblyMetadata.CreateFromFile(typeof(ProtoMemberAttribute).Assembly.Location).GetReference());
  248. references.Add(AssemblyMetadata.CreateFromFile(typeof(BsonDefaultValueAttribute).Assembly.Location).GetReference());
  249. references.Add(AssemblyMetadata.CreateFromFile(typeof(IConfig).Assembly.Location).GetReference());
  250. references.Add(AssemblyMetadata.CreateFromFile(typeof(Attribute).Assembly.Location).GetReference());
  251. references.Add(AssemblyMetadata.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")).GetReference());
  252. references.Add(AssemblyMetadata.CreateFromFile(Path.Combine(assemblyPath, "System.dll")).GetReference());
  253. references.Add(AssemblyMetadata.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")).GetReference());
  254. references.Add(AssemblyMetadata.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")).GetReference());
  255. references.Add(AssemblyMetadata.CreateFromFile(Path.Combine(assemblyPath, "netstandard.dll")).GetReference());
  256. references.Add(AssemblyMetadata.CreateFromFile(typeof(ISupportInitialize).Assembly.Location).GetReference());
  257. CSharpCompilation compilation = CSharpCompilation.Create(
  258. null,
  259. syntaxTrees.ToArray(),
  260. references.ToArray(),
  261. new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
  262. using MemoryStream memSteam = new MemoryStream();
  263. EmitResult emitResult = compilation.Emit(memSteam);
  264. if (!emitResult.Success)
  265. {
  266. StringBuilder stringBuilder = new StringBuilder();
  267. foreach (Diagnostic t in emitResult.Diagnostics)
  268. {
  269. stringBuilder.AppendLine(t.GetMessage());
  270. }
  271. throw new Exception($"动态编译失败:\n{stringBuilder}");
  272. }
  273. memSteam.Seek(0, SeekOrigin.Begin);
  274. Assembly ass = Assembly.Load(memSteam.ToArray());
  275. string dir = GetProtoDir(configType);
  276. if (!Directory.Exists(dir))
  277. {
  278. Directory.CreateDirectory(dir);
  279. }
  280. foreach (string protoName in protoNames)
  281. {
  282. Type type = ass.GetType($"ET.{protoName}Category");
  283. Type subType = ass.GetType($"ET.{protoName}");
  284. Serializer.NonGeneric.PrepareSerializer(type);
  285. Serializer.NonGeneric.PrepareSerializer(subType);
  286. string json = File.ReadAllText(Path.Combine(string.Format(jsonDir, configType), $"{protoName}.txt"));
  287. object deserialize = BsonSerializer.Deserialize(json, type);
  288. string path = Path.Combine(dir, $"{protoName}Category.bytes");
  289. using FileStream file = File.Create(path);
  290. Serializer.Serialize(file, deserialize);
  291. }
  292. }
  293. }
  294. }