Program.cs 12 KB

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