ImportExerciseController.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. using Azure.Storage.Blobs.Models;
  2. using Azure.Storage.Sas;
  3. using HTEXLib;
  4. using HTEXLib.Builders;
  5. using HTEXLib.Helpers.ShapeHelpers;
  6. using Microsoft.AspNetCore.Hosting;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.Mvc;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Globalization;
  12. using System.IdentityModel.Tokens.Jwt;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Net.Http;
  16. using System.Text;
  17. using System.Text.Json;
  18. using System.Threading.Tasks;
  19. using TEAMModelOS.SDK.Models;
  20. using TEAMModelOS.Models.Dto;
  21. using TEAMModelOS.Models.PowerPoint;
  22. using TEAMModelOS.SDK;
  23. using TEAMModelOS.SDK.Context.Constant;
  24. using TEAMModelOS.SDK.Context.Constant.Common;
  25. using TEAMModelOS.SDK.DI;
  26. using TEAMModelOS.SDK.Extension;
  27. using TEAMModelOS.SDK.Module.AzureBlob.Configuration;
  28. using TEAMModelOS.SDK.Module.AzureBlob.Container;
  29. using TEAMModelOS.Services;
  30. using TEAMModelOS.Servicess.Exam;
  31. using TEAMModelOS.Servicess.PowerPoint.Implement;
  32. namespace TEAMModelOS.Controllers
  33. {
  34. [Route("common/import")]
  35. [ApiController]
  36. public class ImportExerciseController : BaseController
  37. {
  38. public HtexGenerator htexGenerator { get; set; }
  39. // private readonly IHtexService htexService;
  40. private readonly AzureStorageFactory _azureStorage;
  41. private readonly IWebHostEnvironment _webHostEnvironment;
  42. private List<LangConfig> langConfigs { get; set; }
  43. private readonly IHttpClientFactory _clientFactory;
  44. public ImportExerciseController( AzureStorageFactory azureStorage, IWebHostEnvironment webHostEnvironment, HtexGenerator htexGenerator, IHttpClientFactory clientFactory)
  45. {
  46. _clientFactory = clientFactory;
  47. this.htexGenerator = htexGenerator;
  48. _webHostEnvironment = webHostEnvironment;
  49. _azureStorage = azureStorage;
  50. string path = _webHostEnvironment.ContentRootPath + "/JsonFile/Core/LangConfig.json";
  51. FileStream fs = new FileStream(path, System.IO.FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  52. StreamReader sr = new StreamReader(fs, System.Text.Encoding.UTF8);
  53. String line;
  54. StringBuilder builder = new StringBuilder();
  55. while ((line = sr.ReadLine()) != null)
  56. {
  57. builder.Append(line.ToString());
  58. }
  59. sr.Close();
  60. string text = builder.ToString();
  61. langConfigs = text.ToObject<List<LangConfig>>();
  62. }
  63. /// <summary>
  64. /// </summary>
  65. /// <param name="request"></param>
  66. /// <returns></returns>
  67. [HttpPost("load-doc")]
  68. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  69. public async Task<BaseResponse> LoadDoc([FromForm] IFormFile file)
  70. {
  71. ResponseBuilder responseBuilder = new ResponseBuilder();
  72. Dictionary<string, object> model = await HtexService.LoadDoc(_azureStorage, file);
  73. return responseBuilder.Data(model).build();
  74. }
  75. /// <summary>
  76. /// {"url":"https://***.blob.core.cn/xxx/1.pptx"}
  77. /// </summary>
  78. /// <param name="request"></param>
  79. /// <returns></returns>
  80. [HttpPost("parse-doc")]
  81. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  82. public async Task<IActionResult> ParsePPTX(JsonElement request)
  83. {
  84. //string id_token = HttpContext.GetXAuth("IdToken");
  85. //if (string.IsNullOrEmpty(id_token)) return BadRequest();
  86. //var jwt = new JwtSecurityToken(id_token);
  87. //if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  88. //var id = jwt.Payload.Sub;
  89. request.TryGetProperty("file", out JsonElement code);
  90. string azureBlobSAS = System.Web.HttpUtility.UrlDecode(code.ToString(), Encoding.UTF8);
  91. (string, string) a = BlobUrlString(azureBlobSAS);
  92. string ContainerName = a.Item1;
  93. string BlobName = a.Item2;
  94. bool flg = IsBlobName(BlobName);
  95. var codes = azureBlobSAS.Split("/");
  96. var file = codes[codes.Length - 1].Split(".");
  97. var FileName = file[0];
  98. var ext = file[1];
  99. if (flg)
  100. {
  101. BlobAuth blobAuth = _azureStorage.GetBlobSasUriRead(ContainerName, BlobName);
  102. var response = await _clientFactory.CreateClient().GetAsync(new Uri(blobAuth.url));
  103. response.EnsureSuccessStatusCode();
  104. Stream stream= await response.Content.ReadAsStreamAsync();
  105. if (ext.ToLower() == "pptx" || ext.ToLower() == "xml")
  106. {
  107. string index = await PPTXTranslator(ContainerName, FileName, stream);
  108. return Ok(new { index = index });
  109. }
  110. else if (ext.ToLower() == "docx" || ext.ToLower() == "doc")
  111. {
  112. return Ok(new { index = "" });
  113. }
  114. else {
  115. return BadRequest("不支持该文件类型的解析!");
  116. }
  117. }
  118. else { return BadRequest("不是正确的Blob链接!"); }
  119. }
  120. private static (string, string) BlobUrlString(string sasUrl)
  121. {
  122. sasUrl = sasUrl.Substring(8);
  123. string[] sasUrls = sasUrl.Split("/");
  124. string ContainerName;
  125. ContainerName = sasUrls[1].Clone().ToString();
  126. string item = sasUrls[0] + "/" + sasUrls[1] + "/";
  127. string blob = sasUrl.Replace(item, "");
  128. return (ContainerName, blob);
  129. }
  130. public static bool IsBlobName(string BlobName)
  131. {
  132. return System.Text.RegularExpressions.Regex.IsMatch(BlobName,
  133. @"(?!((^(con)$)|^(con)\\..*|(^(prn)$)|^(prn)\\..*|(^(aux)$)|^(aux)\\..*|(^(nul)$)|^(nul)\\..*|(^(com)[1-9]$)|^(com)[1-9]\\..*|(^(lpt)[1-9]$)|^(lpt)[1-9]\\..*)|^\\s+|.*\\s$)(^[^\\\\\\:\\<\\>\\*\\?\\\\\\""\\\\|]{1,255}$)");
  134. }
  135. /// <summary>
  136. ///
  137. /// </summary>
  138. /// <param name="request"></param>
  139. /// <returns></returns>
  140. [HttpPost("upload-pptx")]
  141. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  142. public async Task<IActionResult> UploadPPTX([FromForm] IFormFile file)
  143. {
  144. string id_token = HttpContext.GetXAuth("IdToken");
  145. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  146. var jwt = new JwtSecurityToken(id_token);
  147. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  148. var id = jwt.Payload.Sub;
  149. if (FileType.GetExtention(file.FileName).ToLower().Equals("pptx") || FileType.GetExtention(file.FileName).ToLower().Equals("xml"))
  150. {
  151. string FileName = file.FileName.Split(".")[0];
  152. Stream streamFile = file.OpenReadStream();
  153. string index = await PPTXTranslator(id, FileName, streamFile);
  154. return Ok(new { index = index });
  155. }
  156. else {
  157. return BadRequest("type is not pptx or xml !");
  158. }
  159. }
  160. /// <summary>
  161. /// docUrl
  162. /// folder
  163. /// shaCode
  164. ///
  165. /// UploadWord
  166. /// </summary>
  167. /// <param name="request"></param>
  168. /// <returns></returns>
  169. [HttpPost("upload-word")]
  170. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  171. public async Task<IActionResult> UploadWord([FromForm] IFormFile file)
  172. {
  173. // ResponseBuilder responseBuilder = new ResponseBuilder();
  174. if (!FileType.GetExtention(file.FileName).ToLower().Equals("docx"))
  175. {
  176. return BadRequest(new Dictionary<string, object> { {"msg", "type is not docx!" },{ "code",ResponseCode.FAILED} });
  177. }
  178. Dictionary<string, object> model = await ImportExerciseService.UploadWord(_azureStorage, file);
  179. return Ok(model);
  180. }
  181. /// <summary>
  182. /// htmlString AnalyzeHtml
  183. /// </summary>
  184. /// <param name="request"></param>
  185. /// <returns></returns>
  186. [HttpPost("parse-html")]
  187. public IActionResult AnalyzeHtml(JsonElement request)
  188. {
  189. //ResponseBuilder builder = ResponseBuilder.custom();
  190. Dictionary<string, object> dict = new Dictionary<string, object>();
  191. var emobj = request.EnumerateObject();
  192. while (emobj.MoveNext())
  193. {
  194. dict[emobj.Current.Name] = emobj.Current.Value;
  195. }
  196. bool flag = dict.TryGetValue("htmlString", out object htmlString);
  197. bool flagLang = dict.TryGetValue("lang", out object lang);
  198. if (flag && htmlString != null && !string.IsNullOrEmpty(htmlString.ToString()))
  199. {
  200. LangConfig langConfig= langConfigs.Where(x => x.Lang == lang.ToString()).FirstOrDefault();
  201. HtmlAnalyzeService htmlAnalyzeService = new HtmlAnalyzeService(langConfig);
  202. List<ItemInfo> exercises = htmlAnalyzeService.AnalyzeWordAsync(htmlString.ToString());
  203. return Ok(exercises);
  204. }
  205. else
  206. {
  207. return BadRequest();
  208. }
  209. }
  210. /// <summary>
  211. /// htmlString
  212. /// </summary>
  213. /// <param name="request"></param>
  214. /// <returns></returns>
  215. [HttpPost("HtmlToHtex")]
  216. public async Task<IActionResult> HtmlToHtex(JsonElement request)
  217. {
  218. ResponseBuilder builder = ResponseBuilder.custom();
  219. /* Dictionary<string, object> dict = new Dictionary<string, object>();
  220. var emobj = request.EnumerateObject();
  221. while (emobj.MoveNext())
  222. {
  223. dict[emobj.Current.Name] = emobj.Current.Value;
  224. }*/
  225. bool flag = request.TryGetProperty("htmlString", out JsonElement htmlString);
  226. bool flagLang = request.TryGetProperty("lang", out JsonElement lang);
  227. if (flag && htmlString.ToString() != null && !string.IsNullOrEmpty(htmlString.ToString()))
  228. {
  229. LangConfig langConfig = langConfigs.Where(x => x.Lang == lang.ToString()).FirstOrDefault();
  230. HtmlAnalyzeService htmlAnalyzeService = new HtmlAnalyzeService(langConfig);
  231. Models.PowerPoint.Htex exercises = await HtexService.AnalyzeHtmlToHtex(_azureStorage, htmlString.ToString(), lang.ToString(), htmlAnalyzeService);
  232. return Ok(exercises);
  233. }
  234. else
  235. {
  236. return BadRequest("语言设置错误");
  237. }
  238. }
  239. private async Task<string> PPTXTranslator(string id, string FileName, Stream streamFile)
  240. {
  241. var status = await _azureStorage.GetBlobServiceClient().DelectBlobs(id, $"res/{FileName}");
  242. string shaCode = Guid.NewGuid().ToString("N");
  243. HTEXLib.Htex htex = htexGenerator.Generator(streamFile);
  244. htex.name = FileName;
  245. var slides = htex.slides;
  246. List<Task> tasks = new List<Task>();
  247. HTEX hTEX = new HTEX() { name = FileName, size = htex.size, thumbnail = htex.thumbnail, id = shaCode };
  248. Dictionary<string, string> texts = new Dictionary<string, string>();
  249. List<string> shas = new List<string>();
  250. foreach (var slide in slides)
  251. {
  252. string text = JsonHelper.ToJson(slide, ignoreNullValue: false);
  253. string sha = Guid.NewGuid().ToString("N");
  254. texts.Add(sha, text);
  255. shas.Add(sha);
  256. }
  257. Dictionary<string, string> bloburls = new Dictionary<string, string>();
  258. foreach (var key in texts.Keys)
  259. {
  260. tasks.Add(_azureStorage.UploadFileByContainer(id, texts[key], "res", FileName + "/" + key + ".json", false)
  261. .ContinueWith((Task<AzureBlobModel> blob) =>
  262. {
  263. bloburls.Add(key, blob.Result.BlobUrl);
  264. })
  265. );
  266. }
  267. await Task.WhenAll(tasks);
  268. List<Sld> slds = new List<Sld>();
  269. foreach (string sha in shas)
  270. {
  271. slds.Add(new Sld { type = "normal", url = System.Web.HttpUtility.UrlDecode(bloburls[sha], Encoding.UTF8), scoring = null }); ;
  272. }
  273. Dictionary<string, Store> dict = new Dictionary<string, Store>();
  274. List<Task> tasksFiles = new List<Task>();
  275. foreach (var key in htex.stores.Keys)
  276. {
  277. if (key.EndsWith(".wdp") || key.EndsWith(".xlsx"))
  278. {
  279. htex.stores.Remove(key);
  280. continue;
  281. }
  282. var store = htex.stores[key];
  283. Store str = new Store() { path = key, contentType = store.contentType, isLazy = store.isLazy };
  284. if (!store.isLazy && store.contentType != null && ContentTypeDict.extdict.TryGetValue(store.contentType, out string ext) && store.url.Contains(";base64,"))
  285. {
  286. string[] strs = store.url.Split(',');
  287. Stream stream = new MemoryStream(Convert.FromBase64String(strs[1]));
  288. var urlstrs = key.Split("/");
  289. var name = urlstrs[urlstrs.Length - 1];
  290. tasksFiles.Add(_azureStorage.UploadFileByContainer(id, stream, "res", FileName + "/" + name, false)
  291. .ContinueWith((Task<AzureBlobModel> blob) =>
  292. {
  293. str.url = System.Web.HttpUtility.UrlDecode(blob.Result.BlobUrl, Encoding.UTF8);
  294. })
  295. );
  296. }
  297. else
  298. {
  299. str.url = System.Web.HttpUtility.UrlDecode(store.url, Encoding.UTF8);
  300. }
  301. dict.TryAdd(key, str);
  302. }
  303. await Task.WhenAll(tasksFiles);
  304. hTEX.stores = dict;
  305. hTEX.slides = slds;
  306. var blob= await _azureStorage.UploadFileByContainer(id, JsonHelper.ToJson(hTEX, ignoreNullValue: false), "res", FileName + "/" + "index.json", false);
  307. return System.Web.HttpUtility.UrlDecode(blob.BlobUrl, Encoding.UTF8);
  308. }
  309. }
  310. public class HTEX {
  311. public string id { get; set; }
  312. public string version { get; set; } = "1.0.20201210";
  313. public string name { get; set; }
  314. public HTEXLib.HtexSize size { get; set; }
  315. public List<Sld> slides { get; set; }
  316. //缩略图
  317. public string thumbnail { get; set; }
  318. // public int page { get; set; }
  319. public Dictionary<string, Store> stores { get; set; }
  320. public List<string> points { get; set; }
  321. public string periodId { get; set; }
  322. public List<string> gradeIds { get; set; }
  323. public string subjectId { get; set; }
  324. public string subjectName { get; set; }
  325. public string score { get; set; }
  326. public string code { get; set; }
  327. public string scope { get; set; }
  328. public int? multipleRule { get; set; }
  329. }
  330. public class Sld {
  331. /// <summary>
  332. /// normal,普通的hte页面 single 单选题 multiple 多选题 judge 判断题 complete 填空题 subjective 问答题 compose 综合题
  333. /// </summary>
  334. public string type { get; set; }
  335. /// <summary>
  336. /// 单页PPTx htex 的解析链接或一个题目的链接
  337. /// </summary>
  338. public string url { get; set; }
  339. /// <summary>
  340. /// 题目的配分,如果为type为normal 及compose ,则 scoring=null
  341. /// </summary>
  342. public Scoring scoring { get; set; }
  343. /// <summary>
  344. /// 单页PPTx htex 的缩略图
  345. /// </summary>
  346. public string thumbnail { get; set; }
  347. }
  348. public class Scoring {
  349. public double score { get; set; }
  350. public List<string> ans { get; set; } = new List<string>();
  351. }
  352. }