using Azure.Messaging.ServiceBus; using Azure.Storage.Blobs.Models; using Microsoft.Azure.Cosmos; using StackExchange.Redis; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; using System.Web; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using static TEAMModelOS.SDK.CoreAPIHttpService; using TEAMModelOS.SDK.Models; using Microsoft.Extensions.Configuration; using System.Net.Http.Headers; using Azure.Storage.Sas; using TEAMModelOS.SDK.Models.Service; using Azure.Core; using TEAMModelOS.SDK.Models.Cosmos; using System.Configuration; using Google.Protobuf.WellKnownTypes; namespace TEAMModelOS.SDK { public static class GenPDFService { public static async Task GenPdf( AzureRedisFactory _azureRedis, AzureCosmosFactory _azureCosmos, IConfiguration _configuration, IHttpClientFactory _httpClient, AzureStorageFactory _azureStorage, DingDing _dingDing, SnowflakeId _snowflakeId,string json ) { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); Console.WriteLine($"開始:{now}"); string apiUri = "http://52.130.252.100:13000"; PDFGenQueue genQueueData =json.ToObject(); RedisValue redisValue = await _azureRedis.GetRedisClient(8).HashGetAsync($"PDFGen:{genQueueData.sessionId}", genQueueData.id); PDFGenRedis genRedis = null; if (redisValue!=default) { genRedis = redisValue.ToString().ToObject(); //计算等待了多久的时间才开始生成。 var wait = now- genRedis.join; genRedis.wait = wait; genRedis.status= 1; await _azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{genQueueData.sessionId}", genQueueData.id, genRedis.ToJsonString()); var client = _httpClient.CreateClient(); ///再加5秒 client.Timeout= TimeSpan.FromMilliseconds(genQueueData.timeout+ genQueueData.delay+5000); try { string urlpdf = $"{apiUri}/api/pdf"; string jsonElement = new { pageUrl = genQueueData.pageUrl, timeout = genQueueData.timeout, delay = genQueueData.delay, checkPageCompleteJs = genQueueData.checkPageCompleteJs }.ToJsonString(); var request = new HttpRequestMessage { Method = new HttpMethod("POST"), RequestUri = new Uri(urlpdf), Content = new StringContent(jsonElement) }; var mediaTypeHeader = new MediaTypeHeaderValue("application/json") { CharSet = "UTF-8" }; request.Content.Headers.ContentType = mediaTypeHeader; HttpResponseMessage responseMessage = await client.SendAsync(request); if (responseMessage.IsSuccessStatusCode) { string content = await responseMessage.Content.ReadAsStringAsync(); JsonNode jsonNode = content.ToObject(); var code = jsonNode["code"]; var file = jsonNode["data"]?["file"]; if (code!=null && $"{code}".Equals("0") && file!= null && !string.IsNullOrWhiteSpace($"{file}")) { try { Stream stream = await client.GetStreamAsync($"{apiUri}/{file}"); Uri uri = new Uri(genQueueData.pageUrl); var query = HttpUtility.ParseQueryString(uri.Query); string? url = query["url"]; if (!string.IsNullOrWhiteSpace(url)) { url= HttpUtility.UrlDecode(url); uri = new Uri(url); string host = uri.Host; var blobServiceClient = _azureStorage.GetBlobServiceClient(); if (blobServiceClient.Uri.Host.Equals(host)) { // 获取容器名,它是路径的第一个部分 string containerName = uri.Segments[1].TrimEnd('/'); // 获取文件的完整同级目录,这是文件路径中除了文件名和扩展名之外的部分 // 由于文件名是路径的最后一个部分,我们可以通过连接除了最后一个部分之外的所有部分来获取目录路径 string directoryPath = string.Join("", uri.Segments, 2, uri.Segments.Length - 3); string? fileName = Path.GetFileNameWithoutExtension(uri.AbsolutePath); string blobPath = $"{directoryPath}{fileName}.pdf"; var blockBlob = _azureStorage.GetBlobContainerClient(containerName).GetBlobClient(blobPath); string content_type = "application/octet-stream"; ContentTypeDict.dict.TryGetValue(".pdf", out string? contenttype); if (!string.IsNullOrEmpty(contenttype)) { content_type = contenttype; } await blockBlob.UploadAsync(stream, true); blockBlob.SetHttpHeaders(new BlobHttpHeaders { ContentType = content_type }); genRedis.blob= blockBlob.Name; genRedis.status=2; } else { genRedis.status=6; } } else { genRedis.status=6; } } catch (Exception ex) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,GenPDF()\n{ex.Message}\n{ex.StackTrace}\n\n{json}", GroupNames.醍摩豆服務運維群組); genRedis.status=6; } } else { if (code!= null && $"{code}".Equals("99999")) { genRedis.status= 4; } else { genRedis.status= 3; } } } else { genRedis.status= 3; } } catch (TaskCanceledException ex) { if (ex.CancellationToken.IsCancellationRequested) { // Console.WriteLine("请求被取消。"); genRedis.status= 5; } else { //Console.WriteLine("请求超时。"); genRedis.status= 4; } } catch (Exception ex) { genRedis.status=3; await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,GenPDF()\n{ex.Message}\n{ex.StackTrace}\n\n{json}", GroupNames.醍摩豆服務運維群組); } } else { genRedis= new PDFGenRedis { id =genQueueData.id, status= 5, cost=0, join=now, wait=0, name= genQueueData.name }; //被取消的 } long nowNew = DateTimeOffset.Now.ToUnixTimeMilliseconds(); genRedis.cost=nowNew-now; await _azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{genQueueData.sessionId}", genQueueData.id, genRedis.ToJsonString()); //如果全部 生成,需要发送通知 HashEntry[] datas = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"PDFGen:{genQueueData.sessionId}"); List dbgenRedis = new List(); List notifyUsers = new List(); string taskName = string.Empty; string taskType = string.Empty; if (datas!= null && datas.Length > 0) { foreach (var item in datas) { if (!$"{item.Name}".Contains("notifyUsers")) { dbgenRedis.Add(item.Value.ToString().ToObject()); } else { var jsonData = item.Value.ToString().ToObject(); notifyUsers= jsonData.GetProperty("notifyUsers").ToObject>(); taskType = jsonData.GetProperty("taskType").ToString(); taskName = jsonData.GetProperty("taskName").ToString(); } } } if (notifyUsers.IsNotEmpty()) { string lang = Environment.GetEnvironmentVariable("Option:Location")!.Contains("China") ? "zh-cn" : "en-us"; string sql = $"select c.id, c.name ,c.lang as code from c where c.id in ({string.Join(",", notifyUsers.Select(x => $"'{x}'"))})"; List idNameCodes = new List(); await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher) .GetItemQueryIteratorSql(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") })) { idNameCodes.Add(item); } idNameCodes.FindAll(x => string.IsNullOrWhiteSpace(x.code) || (!x.code.Equals("zh-cn") && !x.code.Equals("zh-tw") && !x.code.Equals("en-us"))).ForEach(x => { x.code = lang; }); var clientID = _configuration.GetValue("HaBookAuth:CoreService:clientID"); var clientSecret = _configuration.GetValue("HaBookAuth:CoreService:clientSecret"); var url = _configuration.GetValue("HaBookAuth:CoreAPI"); string location = "China"; if (Environment.GetEnvironmentVariable("Option:Location")!.Contains("China")) { location = "China"; } else if (Environment.GetEnvironmentVariable("Option:Location")!.Contains("Global")) { location = "Global"; } var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location); var client = _httpClient.CreateClient(); if (client.DefaultRequestHeaders.Contains("Authorization")) { client.DefaultRequestHeaders.Remove("Authorization"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}"); } else { client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}"); } //检查dbgenRedis的状态是否全部已经不是0和1 var unfinished = dbgenRedis.FindAll(x => (x.status==0||x.status==1)); if (unfinished==null || unfinished.Count==0) { // 数据大于60一个班,发送报告的总耗时等概要信息, 小于60的发送 生成报告的细则消息,小于五个的,可以列出报告的链接. long joinTime = dbgenRedis.Min(x => x.join); long totalTime = (now-joinTime)/1000; long costTime = dbgenRedis.Sum(x => x.cost)/1000; long avgTime = costTime / dbgenRedis.Count; long maxTime = dbgenRedis.Max(x => x.cost)/1000; long minTime = dbgenRedis.Min(x => x.cost)/1000; long statusOk = dbgenRedis.Where(x => x.status==2).Count(); long statusFailed = dbgenRedis.Where(x => x.status!=2).Count(); string key = "pdf-gen-notify-higher60"; if (dbgenRedis.Count>60) { key = "pdf-gen-notify-higher60"; } else { key ="pdf-gen-notify-below60"; } foreach (var teacher in idNameCodes) { string path = Path.Combine("", $"Lang/{teacher.code}.json"); var sampleJson = File.ReadAllText(path); JsonElement jsonElement = sampleJson.ToObject(); JsonElement msgsJson = jsonElement.GetProperty(key); List msgs = msgsJson.ToObject>(); string msg1 = msgs[1].Replace("{tmdname}", teacher.name).Replace("{taskName}", taskName).Replace("{totalTime}", $"{totalTime}") .Replace("{avgTime}", $"{avgTime}").Replace("{maxTime}", $"{maxTime}").Replace("{minTime}", $"{minTime}").Replace("{statusOk}", $"{statusOk}").Replace("{statusFailed}", $"{statusFailed}"); StringBuilder sb = new StringBuilder($"{msg1}"); if (dbgenRedis.Count<=60) { string status = string.Empty; dbgenRedis.ForEach(x => { switch (x.status) { case 0: status= teacher.code.Equals("zh-cn") ? "未执行" : teacher.code.Equals("zh-tw") ? "未執行" : "unexecuted"; break; case 1: status= teacher.code.Equals("zh-cn") ? "执行中" : teacher.code.Equals("zh-tw") ? "執行中" : "executing"; break; case 2: status= teacher.code.Equals("zh-cn") ? "成功" : teacher.code.Equals("zh-tw") ? "成功" : "success"; break; case 3: status= teacher.code.Equals("zh-cn") ? "失败" : teacher.code.Equals("zh-tw") ? "失敗" : "failed"; break; case 4: status= teacher.code.Equals("zh-cn") ? "超时" : teacher.code.Equals("zh-tw") ? "超時" : "timeout"; break; case 5: status= teacher.code.Equals("zh-cn") ? "取消" : teacher.code.Equals("zh-tw") ? "取消" : "canceled"; break; case 6: status= teacher.code.Equals("zh-cn") ? "存放异常" : teacher.code.Equals("zh-tw") ? "存放異常" : "SaveError"; break; default: status= teacher.code.Equals("zh-cn") ? "失败" : teacher.code.Equals("zh-tw") ? "失敗" : "failed"; break; } string msg2 = msgs[2].Replace("{studentName}", x.name).Replace("{status}", $"{status}").Replace("{wait}", $"{x.wait/1000}").Replace("{cost}", $"{x.cost/1000}").Replace("{total}", $"{(x.wait+x.cost)/1000}"); sb.Append(msg2); }); } NotifyData notifyData = new NotifyData { hubName = "hita5", sender = "IES", tags = new List { $"{teacher.id}_{Constant.NotifyType_IES5_Course}" }, title = msgs[0], eventId = $"{key}_{_snowflakeId.NextId()}", eventName =msgs[0], data = "{\"value\":{}}", body=sb.ToString(), }; string result = ""; try { HttpResponseMessage responseMessage = await client.PostAsJsonAsync($"{url}/service/PushNotify", notifyData); if (responseMessage.StatusCode == HttpStatusCode.OK) { string content = await responseMessage.Content.ReadAsStringAsync(); result = content; } else { result = $"{responseMessage.StatusCode},推送返回的状态码。"; } } catch (Exception exm) { _= _dingDing.SendBotMsg($"{location}站点发送消息异常,{exm.Message}\n{exm.StackTrace}:\n{url}/service/PushNotify \nheader: {token.AccessToken} \nresult:{result}\n params:{notifyData.ToJsonString()}", GroupNames.成都开发測試群組); } } } } Console.WriteLine($"結束:{now}"); return; } /// /// 加入PDF生成队列服务 /// https://github.com/wuxue107/bookjs-eazy https://github.com/wuxue107/screenshot-api-server /// /// /// /// public static async Task<(int total,int add )> AddGenPdfQueue( AzureRedisFactory azureRedis, GenPDFData data,ClientDevice device) { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); List genRedis = new List(); List dbgenRedis = new List(); HashEntry[] datas = await azureRedis.GetRedisClient(8).HashGetAllAsync($"PDFGen:{data.sessionId}"); List notifyUsers = new List(); if (datas!= null && datas.Length > 0) { foreach (var item in datas) { if (!$"{item.Name}".Contains("notifyUsers")) { dbgenRedis.Add(item.Value.ToString().ToObject()); } else { var json = item.Value.ToString().ToObject(); notifyUsers= json.GetProperty("notifyUsers").ToObject>(); } } } if (data.notifyUsers.IsNotEmpty()) { notifyUsers.AddRange(data.notifyUsers); notifyUsers= notifyUsers.Distinct().ToList(); } var dataVal = new GenPDFSchema { notifyUsers= notifyUsers, taskName= data.taskName, taskType = data.taskType }; //Redis记录需要通知的用户 await azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{data.sessionId}", "notifyUsers",JsonSerializer.Serialize(dataVal)); //Redis记录需要生成PDF的数量 int countProcess= 0; foreach (var item in data.datas) { var dbData = dbgenRedis.Find(x => x.id.Equals(item.id)); if (dbData!=null) { //if (dbData.status==0|| dbData.status==1) //{ // //不变的 // countProcess+=1; //} //else { //需要变更的 dbData.status = 0; dbData.cost=0; dbData.join= now; dbData.wait=0; dbData.name=item.name; dbData.url= item.url; dbData.scope = data.scope; dbData.owner= data.owner; dbData.env= data.env; genRedis.Add(dbData); } } else { genRedis.Add(new PDFGenRedis { id = item.id, status=0, cost=0, wait=0, join=now, name=item.name, url= item.url, scope=data.scope, owner= data.owner, env= data.env, }); } } //过期时间 当前个数+ reddis的个数 foreach (var item in genRedis) { countProcess+=1; await azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{data.sessionId}", item.id, item.ToJsonString()); PDFGenQueue genQueue = new PDFGenQueue { id = item.id, checkPageCompleteJs= data.checkPageCompleteJs, delay=data.delay, html= data.html, pageUrl= $"{data.pageUrl}?url={HttpUtility.UrlEncode(item.url)}", sessionId= data.sessionId, timeout =data.timeout, name=item.name, env= data.env, }; //string message = JsonSerializer.Serialize(genQueue); //从头部压入元素,队列先进先出 await azureRedis.GetRedisClient(8).ListLeftPushAsync($"PDFGen:Queue:{device.deviceId}", genQueue.ToJsonString()); //var serviceBusMessage = new ServiceBusMessage(message); //serviceBusMessage.ApplicationProperties.Add("name", "BlobRoot"); //await azureServiceBus.GetServiceBusClient().SendMessageAsync("dep-genpdf", serviceBusMessage); } long expire = (data.delay + data.timeout) * countProcess+30*60*1000; var tiemSpan = TimeSpan.FromMilliseconds(expire); await azureRedis.GetRedisClient(8).KeyExpireAsync($"PDFGen:{data.sessionId}", tiemSpan); return ( countProcess , genRedis.Count() ); } public static async Task<(List studentPdfs, List artResults, ArtEvaluation art)> GenArtStudentPdf( AzureRedisFactory _azureRedis, AzureCosmosFactory _azureCosmos, CoreAPIHttpService _coreAPIHttpService, DingDing _dingDing, AzureStorageFactory _azureStorage, IConfiguration _configuration, List studentIds,string _artId,string _schoolId,string headLang) { try { string _schoolCode = $"{_schoolId}"; ArtEvaluation art = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync($"{_artId}", new PartitionKey($"Art-{_schoolId}")); (List studentPdfs, List artResults) = await ArtService.GenStuArtPDF(studentIds, $"{_artId}", art, $"{_schoolId}", $"{headLang}", _azureCosmos, _coreAPIHttpService, _dingDing); foreach (var artResult in artResults) { if (artResult.pdf == null || string.IsNullOrWhiteSpace(artResult.pdf.blob) || string.IsNullOrWhiteSpace(artResult.pdf.url)) { artResult.pdf = new Attachment { // name = $"{artResult.studentId}.pdf", extension = "pdf", type = "doc", cnt = artResult.school, prime = false,//此处的作用是判断是否已经生成OK. }; } else { artResult.pdf.prime = false;//此处的作用是判断是否已经生成OK. } await _azureRedis.GetRedisClient(8).HashSetAsync($"ArtPDF:{_artId}:{_schoolCode}", artResult.studentId, artResult.ToJsonString()); } //2个小时。 await _azureRedis.GetRedisClient(8).KeyExpireAsync($"ArtPDF:{_artId}:{_schoolCode}", new TimeSpan(2, 0, 0)); List> uploads = new List>(); studentPdfs.ForEach(x => { x.blob = $"art/{x.artId}/report/{x.studentId}.json"; var urlSas = _azureStorage.GetBlobSAS($"{_schoolCode}", x.blob, BlobSasPermissions.Write|BlobSasPermissions.Read, hour: 24); x.blobFullUrl=urlSas.fullUri; uploads.Add(_azureStorage.GetBlobContainerClient($"{_schoolCode}").UploadFileByContainer(x.ToJsonString(), "art", $"{x.artId}/report/{x.studentId}.json", true)); }); var uploadJsonUrls = await Task.WhenAll(uploads); return (studentPdfs, artResults,art); } catch (Exception ex) { await _dingDing.SendBotMsg($"{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組); } return (null, null, null); } /// /// 生成PDF导出模板的数据文件。 /// /// /// /// /// /// /// /// /// public static async Task PushScreenTask(AzureRedisFactory _azureRedis, IConfiguration _configuration, string _artId, ArtEvaluation art, List studentPdfs,IHttpClientFactory _httpClient,DingDing dingDing) { string env = ScreenConstant.env_release; if (_configuration.GetValue("Option:Location").Contains("Test", StringComparison.OrdinalIgnoreCase) || _configuration.GetValue("Option:Location").Contains("Dep", StringComparison.OrdinalIgnoreCase)) { env = ScreenConstant.env_develop; } string ComplexAPI= _configuration.GetValue("HTEX.Complex.API"); try { var client= _httpClient.CreateClient(); client.Timeout= new TimeSpan(0, 0, 30); HttpResponseMessage message = await client.PostAsJsonAsync($"{ComplexAPI}/api/screen/push-task", new GenPDFData { env =env, timeout=30000, delay=1000, checkPageCompleteJs=true, sessionId=$"{_artId}", taskName = art.name, taskType="Art", owner=art.owner, scope=art.scope, pageUrl="https://teammodeltest.blob.core.chinacloudapi.cn/0-public/bookjs/art/index.html", datas= studentPdfs.Select(x => new PDFData { id= x.studentId, name=x.studentName, url =x.blobFullUrl }).ToList() }); if (message.IsSuccessStatusCode) { } else { await dingDing.SendBotMsg($"艺术评测任务添加接口状态返回异常,状态:{message.StatusCode}", GroupNames.成都开发測試群組); } } catch(Exception ex){ await dingDing.SendBotMsg($"艺术评测任务添加异常:{ex.Message},{ex.StackTrace}", GroupNames.成都开发測試群組); } //Console.WriteLine($"{addData.total},{addData.add}"); } } public class GenPDFData { /// /// 数据装载后的页面 要制作为PDF的网页 (pageUrl 、html 参数二选一) /// public string pageUrl { get; set; } /// /// 要截图的网页HTML (pageUrl 、html 参数二选一) "html" : "
bookjs-eazy
", ///
public string html { get; set; } /// /// 超时时间 /// public long timeout { get; set; } = 30000; /// /// 页面完成后(checkPageCompleteJs返回为true后)延迟的时间,可选,默认:0 /// public long delay { get; set; } = 2000; /// /// // 检查页面是否渲染完成的js表达式,可选,默认: "true" /// public bool checkPageCompleteJs { get; set; } /// /// 生成会话id, 活动id /// public string sessionId { get; set; } public List datas { get; set; } = new List(); /// /// 通知用户 /// public List notifyUsers { get; set; } = new List(); /// /// 活动的名称 /// public string taskName { get; set; } /// /// 艺术评测报告Art,评测报告Exam,问卷报告Survey,投票报告Vote,为空 则无法回调更新状态 /// public string taskType { get; set;} /// /// 数据所有者 /// public string owner { get; set; } /// /// 数据范围 /// public string scope { get; set; } public string env { get; set; } } public class GenPDFSchema { public List notifyUsers { get; set; } = new List(); public string taskName { get; set; } public string taskType { get; set; } } public class PDFData{ /// /// 学生id /// public string id { get; set; } /// /// 数据链接 /// public string url { get; set; } /// /// 学生名称 /// public string name { get; set; } } /// /// redis 存储数据,超时时间设置为status=0 的count timeout* count+ delay*count /// public class PDFGenRedis { /// /// 学生id /// public string id { get; set; } /// /// 学生名称 /// public string name { get; set; } /// /// 执行生成 毫秒 /// public long cost { get; set;} /// /// 等候时间 /// public long wait { get; set;} /// /// 加入时间 /// public long join { get; set; } /// /// 上传成功后的文件 /// public string blob { get; set; } /// /// 數據的url /// public string url { get; set; } /// /// 0 未执行,1 执行中,2 执行成功,3 执行失败,4超时,5 取消,6 存放异常 /// public int status { get; set; } = 0; /// /// 状态信息 /// public string msg { get; set; } /// /// 数据所有者 /// public string owner { get; set; } /// /// 数据范围 /// public string scope { get; set; } /// /// 环境变量 /// public string env { get; set; } } /// /// // 拼接上接口的前缀 http://localhost:3000/ 就是完整PDF地址 // http://localhost:3000/pdf/1614458263411-glduu.pdf // 拼接上接口的前缀 http://localhost:3000/download/可以就可生成在浏览器上的下载链接 // http://localhost:3000/download/pdf/1614458263411-glduu.pdf // 拼接上http://localhost:3000/static/js/pdfjs/web/viewer.html?file=/pdf/1614458263411-glduu.pdf // 可使用pdfjs库进行预览 // //## -e MAX_BROWSER=[num] 环境变量可选,最大的puppeteer实例数,忽略选项则默认值:1 , 值auto:[可用内存]/200M //## -e PDF_KEEP_DAY=[num] 自动删除num天之前产生的文件目录,默认0: 不删除文件 //sudo docker run -p 13000:3000 -td --rm -e MAX_BROWSER=2 -e PDF_KEEP_DAY=1 -v ${PWD}:/screenshot-api-server/public --name=screenshot-api-server wuxue107/screenshot-api-server /// public class PDFGenQueue { /// /// 环境变量 /// public string env { get; set;} /// /// 姓名 /// public string name { get; set; } /// /// 学生id /// public string id { get; set; } /// /// 数据装载后的页面 要制作为PDF的网页 (pageUrl 、html 参数二选一) /// public string pageUrl { get; set; } /// /// 要截图的网页HTML (pageUrl 、html 参数二选一) "html" : "
bookjs-eazy
", ///
public string html { get; set; } /// /// 超时时间 /// public long timeout { get; set; } = 30000; /// /// 页面完成后(checkPageCompleteJs返回为true后)延迟的时间,可选,默认:0 /// public long delay { get; set; } = 2000; /// /// // 检查页面是否渲染完成的js表达式,可选,默认: "true" /// public bool checkPageCompleteJs { get; set; } /// /// 生成会话id, 活动id /// public string sessionId { get; set; } /// /// blob的sas /// public string blobSas { get; set; } /// /// blob名称 /// public string blobName { get; set; } /// /// 容器名称 /// public string cntName { get; set; } /// /// 完整blob地址 /// public string blobFullUrl { get; set; } } public static class HybridConstant { public static readonly string busy = "busy"; public static readonly string idle = "idle"; public static readonly string error = "error"; public static readonly string offline = "offline"; public static readonly string grant_type = "hybrid-cloud"; public static readonly string env_release = "release"; public static readonly string env_develop = "develop"; /// /// 冗余时间 /// public static readonly long time_excess = 5000; } public class HybridClient : ClientDevice { /// /// 授权类型,hybrid-cloud /// public string? grant_type { get; set; } /// /// 客户端id /// public string? clientid { get; set; } /// /// SignalR的连接ID 不建议暴露。 /// public string? connid { get; set; } /// /// 状态 busy 忙碌,free 空闲,down 离线,error 错误 /// public string? status { get; set; } /// /// 最后更新时间 /// public long last_time { get; set; } /// /// 超时时间,单位毫秒 /// public long timeout { get; set; } = 30000; /// /// 延迟时间,单位毫秒 /// public long delay { get; set; } = 3000; } public class ScreenClient : ClientDevice { /// /// 授权类型,bookjs_api /// public string? grant_type { get; set; } /// /// 客户端id /// public string? clientid { get; set; } /// /// SignalR的连接ID 不建议暴露。 /// public string? connid { get; set; } /// /// 状态 busy 忙碌,free 空闲,down 离线,error 错误 /// public string? status { get; set; } /// /// 最后更新时间 /// public long last_time { get; set; } /// /// 任务完成数 /// public int taskComplete { get; set; } /// /// 超时时间,单位毫秒 /// public long timeout { get; set; } = 30000; /// /// 延迟时间,单位毫秒 /// public long delay { get; set; } = 3000; /// /// PDF服务地址 /// public string? screenUrl { get; set; } } public class SignalRClient { /// /// 授权类型,bookjs_api /// public string? grant_type { get; set; } /// /// 客户端id /// public string? clientid { get; set; } /// /// SignalR的连接ID 不建议暴露。 /// public string? connid { get; set; } public string? serverid { get; set;} } public interface IClient { Task ReceiveMessage(MessageBody message); Task ReceiveConnection(MessageBody message); Task ReceiveDisConnection(MessageBody message); } public abstract class MessageBody { public MessageBody() { time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } /// /// 连接id /// public virtual string? connid { get; set; } /// /// 客户端id /// public virtual string? clientid { get; set; } /// /// 状态 busy 忙碌,free 空闲,down 离线,error 错误 /// public virtual string? status { get; set; } /// /// 消息内容 /// public virtual string? content { get; set; } /// /// 消息创建时间 /// public virtual long time { get; } /// /// 授权类型,bookjs_api /// public virtual string? grant_type { get; set; } /// /// 消息类型 /// public virtual MessageType message_type { get; set; } } /// /// 连接消息 /// public class ConnectionMessage : MessageBody { } /// /// 断开连接消息 /// public class DisConnectionMessage : MessageBody { } /// /// 业务处理消息 /// public class ScreenProcessMessage : MessageBody { public string msg { get; set; } public int result { get; set; } } public static class ScreenConstant { public static readonly string busy = "busy"; public static readonly string idle = "idle"; public static readonly string error = "error"; public static readonly string offline = "offline"; public static readonly string grant_type = "bookjs_api"; public static readonly string env_release = "release"; public static readonly string env_develop = "develop"; /// /// 冗余时间 /// public static readonly long time_excess = 5000; } public enum MessageType { conn_success,//连接成功 conn_error,// 连接失败 task_send_success,// 任务发送成功 task_send_error,// 任务发送失败 task_execute_success,// 任务执行成功 task_execute_error,// 任务执行失败 } public class ClientDevice { /// /// 机器名 /// public string? name { get; set; } /// /// 操作系统 /// public string? os { get; set; } /// /// CPU核心数量 /// public int cpu { get; set; } /// /// 内存大小 /// public long ram { get; set;} /// /// 远程ip /// public string? remote { get; set; } /// /// 端口,可能有多个端口 /// public string? port { get; set; } /// /// 地区 /// public string? region { get; set; } /// /// 网卡 IP信息 /// public List networks { get; set; } = new List(); /// /// 设备id /// public string? deviceId { get; set; } } public class Network { public string? name { get; set; } public string? mac { get; set; } public string? ip { get; set; } } }