CoreController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using Azure.Storage.Blobs.Models;
  2. using Microsoft.AspNetCore.Authorization;
  3. using Microsoft.AspNetCore.Http;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.Extensions.Options;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Drawing.Imaging;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Net.Http;
  12. using System.Reflection;
  13. using System.Text.Json;
  14. using System.Text.RegularExpressions;
  15. using System.Threading.Tasks;
  16. using TEAMModelOS.Models;
  17. using TEAMModelOS.Models.Request;
  18. using TEAMModelOS.SDK;
  19. using TEAMModelOS.SDK.DI;
  20. using TEAMModelOS.SDK.Extension;
  21. using TEAMModelOS.SDK.Models.Service;
  22. using TEAMModelOS.SDK.PngQuant;
  23. namespace TEAMModelOS.Controllers
  24. {
  25. [Route("core")]
  26. [ApiController]
  27. public class CoreController : ControllerBase
  28. {
  29. private readonly AzureRedisFactory _azureRedis;
  30. private readonly AzureStorageFactory _azureStorage;
  31. private readonly DingDing _dingDing;
  32. private readonly Option _option;
  33. private readonly HttpClient _httpClient;
  34. private readonly IPSearcher _searcher;
  35. public CoreController(IPSearcher searcher,AzureRedisFactory azureRedis, AzureStorageFactory azureStorage , DingDing dingDing, IOptionsSnapshot<Option> option, HttpClient httpClient)
  36. {
  37. _searcher= searcher;
  38. _azureStorage = azureStorage;
  39. _dingDing = dingDing;
  40. _option = option?.Value;
  41. _httpClient = httpClient;
  42. _azureRedis = azureRedis;
  43. }
  44. [HttpPost("apply-school")]
  45. public async Task<IActionResult> ApplySchool(ApplySchool request)
  46. {
  47. string msg = $"有新学校申请。\n" +
  48. $"申请站点:{_option.Location}\n" +
  49. $"申请学校:{request.name}\n" +
  50. $"学制:{string.Join(",", request.period)}\n" +
  51. $"机构代码:{request.orgCode}\n" +
  52. $"所在国家\\地区:{request.area}\n" +
  53. $"申请人:{request.tmdname}({request.tmdid})\n" +
  54. $"联系电话:{request.cellphone}\n" +
  55. $"备注:{request.content}";
  56. await _dingDing.SendBotMsg(msg, GroupNames.AI智慧學校申請通知群);
  57. return Ok(new { msg });
  58. }
  59. [HttpPost("random-code")]
  60. [RequestSizeLimit(100_000_000)] //最大100m左右
  61. public async Task<IActionResult> RandomCode(JsonElement request)
  62. {
  63. if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();//学校编码 或者醍摩豆id
  64. string _num09 = "123456789";
  65. string no = $"{Utils.CreatSaltString(6, _num09)}";
  66. var Expire =DateTime.UtcNow.AddHours(1);
  67. string key = $"Random:Code:{no}-{code}";
  68. await _azureRedis.GetRedisClient(8).StringSetAsync(key, new { code=code,no=no, Expire = Expire.Ticks }.ToJsonString());
  69. _azureRedis.GetRedisClient(8).KeyExpire(key, Expire);
  70. return Ok(new { random=no });
  71. }
  72. [HttpPost("system-info")]
  73. [RequestSizeLimit(100_000_000)] //最大100m左右
  74. public async Task<IActionResult> SystemInfo(JsonElement request)
  75. {
  76. Type attr = this.GetType();
  77. string currentDirectory = Path.GetDirectoryName(attr.Assembly.Location);
  78. Assembly assembly = Assembly.LoadFrom( $"{currentDirectory}\\TEAMModelOS.dll");
  79. var description= assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
  80. //var v1 = Assembly.GetEntryAssembly().GetName().Version;
  81. //var v2 = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
  82. // var description = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
  83. var version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
  84. long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  85. //Console.WriteLine($"Assembly.GetEntryAssembly().GetName().Version: " +
  86. // $"{Assembly.GetEntryAssembly().GetName().Version}");5.2107.12.1
  87. //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version:" +
  88. // $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version}");5.2107.12.1
  89. //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion:" +
  90. // $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion}");5.2107.12
  91. var IpPort = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
  92. if (string.IsNullOrEmpty(IpPort))
  93. {
  94. IpPort = HttpContext.Connection.RemoteIpAddress.ToString();
  95. }
  96. if (IpPort.Contains("::"))
  97. {
  98. IpPort = "127.0.0.1";
  99. }
  100. string ip = IpPort.Split(":")[0];
  101. string region = await _searcher.SearchIpAsync(ip);
  102. return Ok(new { version, description, nowtime , region,ip });
  103. }
  104. /// <summary>
  105. /// 等待P1V3,啟動Dcoker,支持EMF GDI+
  106. /// </summary>
  107. /// <param name="request"></param>
  108. /// <returns></returns>
  109. [HttpPost("convert-emf")]
  110. [RequestSizeLimit(100_000_000)] //最大100m左右
  111. public async Task<IActionResult> ConvertEmf(ImageQuangRequest request)
  112. {
  113. try
  114. {
  115. if (string.IsNullOrWhiteSpace(request.base64)) return BadRequest();
  116. byte[] obase64data = Convert.FromBase64String(request.base64);
  117. using var obase64ms = new MemoryStream(obase64data);
  118. var (isimg, type, dupe) = Utils.ImageValidateByStream(obase64ms);
  119. if (isimg && type.Equals("emf"))
  120. {
  121. var pngimagebase64 = Utils.ConvertEMFtoPNG(obase64ms);
  122. return Ok(pngimagebase64);
  123. }
  124. else
  125. {
  126. return BadRequest("非EMF圖片格式");
  127. }
  128. }
  129. catch (Exception ex)
  130. {
  131. await _dingDing.SendBotMsg($"Core,{_option.Location},convert-emf()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  132. return BadRequest();
  133. }
  134. }
  135. /// <summary>
  136. /// PNG JPF 圖片輕量化
  137. /// </summary>
  138. /// <param name="request">支持html base64標籤解析</param>
  139. /// <returns></returns>
  140. [HttpPost("image-quant")]
  141. //
  142. [RequestSizeLimit(100_000_000)] //最大100m左右
  143. public async Task<IActionResult> ImageQuang(List<ImageQuangRequest> request)
  144. {
  145. if (request.Count < 1) return BadRequest();
  146. Regex rex = new Regex(@"data:(?<key1>image.+?);base64,(?<key2>.+)");
  147. List<object> respons = new List<object>();
  148. var quantizer = new PngQuantizer();
  149. foreach (var item in request)
  150. {
  151. //正則處理,移除空白
  152. string trim = Regex.Replace(item.base64, @"\s", "");
  153. Match match = rex.Match(trim);
  154. string otype = match.Groups["key1"].Value;
  155. string obase64 = match.Groups["key2"].Value;
  156. byte[] obase64data = Convert.FromBase64String(obase64);
  157. using var obase64ms = new MemoryStream(obase64data);
  158. //驗證圖片格式及位深,判斷是否要處理量化演算
  159. var (isimg, type, dupe) = Utils.ImageValidateByStream(obase64ms);
  160. if (isimg && (type.Equals("png") || type.Equals("jpg")) && dupe > 8)
  161. {
  162. string img = string.Empty;
  163. using var quantized = quantizer.QuantizeImage(obase64ms, item.width, item.height); //JPEG,PNG輕量化
  164. if (quantized != null)
  165. {
  166. //檢查是否需要保存原圖到blob
  167. if (!string.IsNullOrWhiteSpace(item.blob))
  168. {
  169. img = $"{Guid.NewGuid():N}.{type}"; //命名新圖片名稱
  170. string Container = item.blob.Substring(0, item.blob.IndexOf("/")); //取得容器名稱
  171. string blobpath = $"{item.blob[(item.blob.Trim('/').IndexOf("/") + 1)..]}/{img}"; //處理路徑,避免多餘的字符
  172. obase64ms.Position = 0;
  173. await _azureStorage.GetBlobContainerClient(Container).GetBlobClient(blobpath).UploadAsync(obase64ms, new BlobHttpHeaders { ContentType = otype });
  174. }
  175. using var nbase64ms = new MemoryStream();
  176. quantized.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式
  177. byte[] data = nbase64ms.ToArray();
  178. string base64 = $"data:image/png;base64,{Convert.ToBase64String(data)}"; //創建新的img src base64
  179. respons.Add(new { base64 = base64, blob = img });
  180. // TODO 下面註解,無法解決保存時,真正改變Format32bppArgb為Format8bppIndexed,需要再研究
  181. //ImageCodecInfo imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(info => info.MimeType == "image/png");
  182. //var parameters = new EncoderParameters(1);
  183. //parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
  184. //quantized.Save(nbase64ms, imageCodecInfo, parameters); //保存為PNG格式
  185. }
  186. else
  187. {
  188. respons.Add(new { base64 = item.base64, blob = "" }); //異常的圖片,原Base64返回
  189. }
  190. }
  191. else
  192. {
  193. respons.Add(new { base64 = item.base64, blob = "" }); //不符合的圖片,原Base64返回
  194. }
  195. }
  196. return Ok(respons);
  197. }
  198. }
  199. public record ApplySchool
  200. {
  201. /// <summary>
  202. /// 学校名称
  203. /// </summary>
  204. public string name { get; set; }
  205. /// <summary>
  206. /// 站点
  207. /// </summary>
  208. public string site { get; set; }
  209. public string area { get; set; }
  210. public string tmdname { get; set; }
  211. public string tmdid { get; set; }
  212. /// <summary>
  213. /// 手机号
  214. /// </summary>
  215. public string cellphone { get; set; }
  216. /// <summary>
  217. /// 备注
  218. /// </summary>
  219. public string content { get; set; }
  220. /// <summary>
  221. /// 学制
  222. /// </summary>
  223. public List<string> period { get; set; } = new List<string>();
  224. /// <summary>
  225. /// 机构代码
  226. /// </summary>
  227. public string orgCode { get; set; }
  228. }
  229. }