CrazyIter_Bin 10 ヶ月 前
コミット
ca5ac3f6c6

+ 267 - 18
TEAMModelOS.Function/IESServiceBusTrigger.cs

@@ -31,6 +31,14 @@ using Microsoft.Azure.Cosmos.Linq;
 using System.Net.Http.Json;
 using System.Threading;
 using System.Text.Json.Nodes;
+using System.Web;
+using Azure.Storage.Blobs;
+using static TEAMModelOS.SDK.CoreAPIHttpService;
+using static Google.Protobuf.Reflection.SourceCodeInfo.Types;
+using System.Net;
+using Newtonsoft.Json;
+using System.Security.Policy;
+using System.Net.Http.Headers;
 namespace TEAMModelOS.Function
 {
     public class IESServiceBusTrigger
@@ -72,10 +80,15 @@ namespace TEAMModelOS.Function
         }
         [Function("GenPDF")]
         public async Task Run(
-         [ServiceBusTrigger("genpdf", Connection = "Azure:ServiceBus:ConnectionString")]
+         [ServiceBusTrigger("dep-genpdf", Connection = "Azure:ServiceBus:ConnectionString" , IsBatched =false) ]
             ServiceBusReceivedMessage message,
          ServiceBusMessageActions messageActions)
         {
+            message.lo
+          // await messageActions.RenewMessageLockAsync(message);
+            long time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+            Console.WriteLine($"開始:{time}");
+            string apiUri = "http://52.130.252.100:13000";
             long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             _logger.LogInformation("Message ID: {id}", message.MessageId);
             _logger.LogInformation("Message Body: {body}", message.Body);
@@ -101,27 +114,101 @@ namespace TEAMModelOS.Function
                     client.Timeout= TimeSpan.FromMilliseconds(genQueueData.timeout+ genQueueData.delay+5000);
                     try
                     {
-                        HttpResponseMessage responseMessage = await client.PostAsJsonAsync("http://52.130.252.100:13000/api/pdf", new { pageUrl = genQueueData.pageUrl, timeout = genQueueData.timeout, delay = genQueueData.delay, checkPageCompleteJs = genQueueData.checkPageCompleteJs });
+                        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<JsonNode>();
                             var code = jsonNode["code"];
-                            var file = jsonNode["code"]?["data"]?["file"];
+                            var file = jsonNode["data"]?["file"];
                             if (code!=null  && $"{code}".Equals("0")  && file!= null  && !string.IsNullOrWhiteSpace($"{file}"))
                             {
-                                //所以步骤执行成功
-                                genRedis.status=2;
+
+                                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.RootElement.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                                    genRedis.status=6;
+                                }
                             }
                             else
                             {
-                                if (code!= null&& $"{code}".Equals("99999"))
+                                if (code!= null && $"{code}".Equals("99999"))
                                 {
                                     genRedis.status= 4;
 
                                 }
+                                else { 
+                                    genRedis.status= 3;
+                                }
                             }
                         }
+                        else { 
+                            genRedis.status= 3;
+                        }
                     }
                     catch (TaskCanceledException ex)
                     {
@@ -144,15 +231,177 @@ namespace TEAMModelOS.Function
                 }
                 else 
                 {
-                    genRedis= new PDFGenRedis { id =genQueueData.id, status= 5, cost=0, join=now, wait=0 };
+                    genRedis= new PDFGenRedis { id =genQueueData.id, status= 5, cost=0, join=now, wait=0,name=  genQueueData .name };
                     //被取消的
                 }
 
-                long  nowNew = DateTimeOffset.Now.ToUnixTimeSeconds();
+                long  nowNew = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                 genRedis.cost=nowNew-now;
                 await _azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{genQueueData.sessionId}", genQueueData.id, genRedis.ToJsonString());
                 //如果全部 生成,需要发送通知
-                // 数据大于60一个班,发送报告的总耗时等概要信息, 小于60的发送 生成报告的细则消息,小于五个的,可以列出报告的链接,
+               
+                HashEntry[] datas = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"PDFGen:{genQueueData.sessionId}");
+                List<PDFGenRedis> dbgenRedis = new List<PDFGenRedis>();
+                List<string> notifyUsers = new List<string>();
+                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<PDFGenRedis>());
+                        }
+                        else
+                        {
+                            var jsonData = item.Value.ToString().ToObject<JsonElement>();
+                            notifyUsers=  jsonData.GetProperty("notifyUsers").ToObject<List<string>>();
+                            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<IdNameCode> idNameCodes = new List<IdNameCode>();
+                    await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                        .GetItemQueryIteratorSql<IdNameCode>(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<string>("HaBookAuth:CoreService:clientID");
+                    var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                    var url = _configuration.GetValue<string>("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>();
+                             JsonElement msgsJson = jsonElement.GetProperty(key);
+                            List<string> msgs = msgsJson.ToObject<List<string>>();
+                            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<string> { $"{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($"結束:{time}");
             }
             catch (Exception ex)
             {
@@ -1128,7 +1377,7 @@ namespace TEAMModelOS.Function
                                     }
                                     catch (Exception ex)
                                     {
-                                        // await _dingDing.SendBotMsg($"{_option.Location}/LessonRecordEvent/课堂记录更新课堂时长出错records/{_lessonId}/Record/.Recording.json\n{ex.Message}\n{ex.StackTrace}{msg}", GroupNames.醍摩豆服務運維群組);
+                                        // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}/LessonRecordEvent/课堂记录更新课堂时长出错records/{_lessonId}/Record/.Recording.json\n{ex.Message}\n{ex.StackTrace}{msg}", GroupNames.醍摩豆服務運維群組);
                                     }
                                     isReplace = true;
                                     break;
@@ -1181,7 +1430,7 @@ namespace TEAMModelOS.Function
                                     {
                                         if (!ex.Message.Contains("The specified blob does not exist"))
                                         {
-                                            await _dingDing.SendBotMsg($"{_option.Location},TimeLine.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
+                                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},TimeLine.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
                                         }
                                     }
                                     //读取Task.json
@@ -1195,7 +1444,7 @@ namespace TEAMModelOS.Function
                                     {
                                         if (!ex.Message.Contains("The specified blob does not exist"))
                                         {
-                                            await _dingDing.SendBotMsg($"{_option.Location},Task.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
+                                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},Task.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
                                         }
                                     }
                                     //读取IRS.json
@@ -1210,14 +1459,14 @@ namespace TEAMModelOS.Function
                                     {
                                         if (!ex.Message.Contains("The specified blob does not exist"))
                                         {
-                                            await _dingDing.SendBotMsg($"{_option.Location},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
+                                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組);
                                         }
                                     }
 
                                     //如果有更新 则去读取/{_lessonId}/IES/base.json
                                     try
                                     {
-                                        // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 收到更新", GroupNames.醍摩豆服務運維群組);
+                                        // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{_lessonId} 收到更新", GroupNames.醍摩豆服務運維群組);
                                         BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{_lessonId}/IES/base.json").DownloadContentAsync();
                                         //attendState
                                         string basejson = baseblobDownload.Content.ToString().Replace("\"Uncall\"", "0").Replace("Uncall", "0");
@@ -1228,7 +1477,7 @@ namespace TEAMModelOS.Function
                                         }
                                         catch (Exception ex)
                                         {
-                                            await _dingDing.SendBotMsg($"{_option.Location},base.json转换异常,{ex.Message}{ex.StackTrace}{basejson},{lessonRecord.id}", GroupNames.成都开发測試群組);
+                                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},base.json转换异常,{ex.Message}{ex.StackTrace}{basejson},{lessonRecord.id}", GroupNames.成都开发測試群組);
                                             //lessonBase = baseblobDownload.Content.ToObjectFromJson<LessonBase>();
                                         }
                                         //await  _dingDing.SendBotMsg($"课例记录文件base.json:{lessonBase.ToJsonString()}", GroupNames.成都开发測試群組);
@@ -1344,7 +1593,7 @@ namespace TEAMModelOS.Function
                                         }
                                         //有上传 base.josn.
                                         lessonRecord.upload = 1;
-                                        // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新完成", GroupNames.醍摩豆服務運維群組);
+                                        // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{_lessonId} 更新完成", GroupNames.醍摩豆服務運維群組);
 
                                         LessonService.DoAutoDeleteSchoolLessonRecord(lessonRecord, scope, client, school, tmdid, teacher, _serviceBus, _azureStorage, _configuration, _coreAPIHttpService, _dingDing, _azureRedis);
                                         long? size = await _azureStorage.GetBlobContainerClient(blobname).GetBlobsSize($"records/{_lessonId}");
@@ -1362,7 +1611,7 @@ namespace TEAMModelOS.Function
                                             size = size.HasValue ? size.Value : 0,
                                         };
                                         await client.GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(bloblog);
-                                        //await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} blob刷新完成!", GroupNames.醍摩豆服務運維群組);
+                                        //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{_lessonId} blob刷新完成!", GroupNames.醍摩豆服務運維群組);
 
                                         await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = "records", name = $"{blobname}" }, _serviceBus, _configuration, _azureRedis);
                                         msgs.Add(update);
@@ -2081,7 +2330,7 @@ namespace TEAMModelOS.Function
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Imei AF error\n{ex.Message}\n{ex.StackTrace}{json.RootElement.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"IES5,{Environment.GetEnvironmentVariable("Option:Location")},Imei AF error\n{ex.Message}\n{ex.StackTrace}{json.RootElement.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
             }
             finally
             {  

ファイルの差分が大きいため隠しています
+ 3 - 1
TEAMModelOS.Function/Lang/en-us.json


+ 3 - 1
TEAMModelOS.Function/Lang/zh-cn.json

@@ -27,5 +27,7 @@
   "art-template-subject_music": "其实音乐殿堂的门槛并没有你想的那么高,对吧?希望你继续努力能够在这里欣赏到更美丽的风景!跳动的音符充满魅力,每个人的生活都离不开音乐,加油吧,相信你在音乐中会有所收获的。",
   "art-template-subject_painting": "你敢于探素,乐于欣赏,在五彩的画笔中快乐成长,如果你再多一点耐心、仔细刻画,一定会更棒!你积极热爱美术,总是充满快乐,希望你能够坚持美术学习,永不放弃,在美术的道路上快乐地成长!",
   "blob-space-school-notify": [ "剩余空间不足", "亲爱的{schoolName}管理员{tmdname}您好,提醒您贵校剩余空间不足{percent}%,请联系官方客服购买空间,详情请登入IES(teammodel.net)查看,如果已经扩容请忽略此通知。" ],
-  "blob-space-private-notify": [ "剩余空间不足", "亲爱的{tmdname}您好,提醒您的帐号{tmdid}授权的剩余空间不足{percent}%,请联系学校管理员配置或是购买个人空间,如果已经扩容请忽略此通知。" ]
+  "blob-space-private-notify": [ "剩余空间不足", "亲爱的{tmdname}您好,提醒您的帐号{tmdid}授权的剩余空间不足{percent}%,请联系学校管理员配置或是购买个人空间,如果已经扩容请忽略此通知。" ],
+  "pdf-gen-notify-higher60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,单个报告生成最长耗时{maxTime}秒,单个报告最短耗时{minTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个" ],
+  "pdf-gen-notify-below60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个。以下是生成报告的详情:", "{studentName}(状态:{status},等待:{wait}秒,耗时:{cost}秒,总耗时:{total}秒)。" ]
 }

+ 3 - 1
TEAMModelOS.Function/Lang/zh-tw.json

@@ -27,5 +27,7 @@
   "art-template-subject_music": "其实音乐殿堂的门槛并没有你想的那么高,对吧?希望你继续努力能够在这里欣赏到更美丽的风景!跳动的音符充满魅力,每个人的生活都离不开音乐,加油吧,相信你在音乐中会有所收获的。",
   "art-template-subject_painting": "你敢于探素,乐于欣赏,在五彩的画笔中快乐成长,如果你再多一点耐心、仔细刻画,一定会更棒!你积极热爱美术,总是充满快乐,希望你能够坚持美术学习,永不放弃,在美术的道路上快乐地成长!",
   "blob-space-school-notify": [ "剩餘空間不足", "親愛的{schoolName}管理員{tmdname}您好,提醒您貴校剩餘空間不足{percent}%,請聯繫官方客服購買空間,詳情請登入IES(teammodel.net)查看,如果已經擴容請忽略此通知。" ],
-  "blob-space-private-notify": [ "剩餘空間不足", "親愛的{tmdname}您好,提醒您的帳號{tmdid}授權的剩餘空間不足{percent}%,請聯繫學校管理員配置或是購買個人空間,如果已經擴容請忽略此通知。" ]
+  "blob-space-private-notify": [ "剩餘空間不足", "親愛的{tmdname}您好,提醒您的帳號{tmdid}授權的剩餘空間不足{percent}%,請聯繫學校管理員配置或是購買個人空間,如果已經擴容請忽略此通知。" ],
+  "pdf-gen-notify-higher60": [ "PDF報告生成通知", "{tmdname}您好,{taskName}的PDF報告已經生成,總耗時(包含等待和生成時間){totalTime}秒,報告產生平均耗時{avgTime}秒,單一報告產生最長耗時{maxTime}秒,單一報告最短耗時{minTime}秒,產生成功{statusOk}個,產生失敗{statusFailed}個" ],
+  "pdf-gen-notify-below60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个。以下是生成报告的详情:", "{studentName}(状态:{status},等待:{wait}秒,耗时:{cost}秒,总耗时:{total}秒)。" ]
 }

+ 59 - 19
TEAMModelOS.SDK/Models/Service/GenPDFService.cs

@@ -5,13 +5,15 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using System.Text;
+using System.Text.Json;
 using System.Threading.Tasks;
+using System.Web;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 
-namespace TEAMModelOS.SDK.Models.Service
+namespace TEAMModelOS.SDK
 {
-    public class GenPDFService
+    public static class GenPDFService
     {
         /// <summary>
         /// 加入PDF生成队列服务
@@ -21,7 +23,7 @@ namespace TEAMModelOS.SDK.Models.Service
         /// <param name="azureRedis"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        public async Task<dynamic> AddGenPdfQueue(AzureServiceBusFactory azureServiceBus , AzureRedisFactory azureRedis, GenPDFData data) 
+        public static async Task<(int total,int add )> AddGenPdfQueue(AzureServiceBusFactory azureServiceBus , AzureRedisFactory azureRedis, GenPDFData data) 
         {
             long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             List<PDFGenRedis> genRedis = new List<PDFGenRedis>();
@@ -37,7 +39,8 @@ namespace TEAMModelOS.SDK.Models.Service
                         dbgenRedis.Add(item.Value.ToString().ToObject<PDFGenRedis>());
                     }
                     else {
-                        notifyUsers= item.Value.ToString().ToObject<List<string>>(); ;
+                        var json = item.Value.ToString().ToObject<JsonElement>();
+                        notifyUsers=  json.GetProperty("notifyUsers").ToObject<List<string>>();
                     }
                 }
             }
@@ -46,8 +49,9 @@ namespace TEAMModelOS.SDK.Models.Service
                 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", notifyUsers.ToJsonString());
+            await azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{data.sessionId}", "notifyUsers",JsonSerializer.Serialize(dataVal));
             //Redis记录需要生成PDF的数量
             int countProcess= 0;
             foreach (var item in data.datas)
@@ -68,35 +72,42 @@ namespace TEAMModelOS.SDK.Models.Service
                         dbData.cost=0;
                         dbData.join= now;
                         dbData.wait=0;
+                        dbData.name=item.name;
                         genRedis.Add(dbData);
                     }
                 }
                 else {
-                    genRedis.Add(new PDFGenRedis { id = item.id, status=0, cost=0, wait=0, join=now });
+                    genRedis.Add(new PDFGenRedis { id = item.id, status=0, cost=0, wait=0, join=now,name=item.name,url= item.url });
                 }
             }
             //过期时间 当前个数+ reddis的个数
-            List<ServiceBusMessage> queueDatas= new List<ServiceBusMessage>();
             foreach (var item in genRedis) 
             {
                 countProcess+=1;
                 await azureRedis.GetRedisClient(8).HashSetAsync($"PDFGen:{data.sessionId}", item.id, item.ToJsonString());
-                queueDatas.Add(new ServiceBusMessage (new PDFGenQueue
+
+                PDFGenQueue genQueue = new PDFGenQueue
                 {
                     id = item.id,
                     checkPageCompleteJs= data.checkPageCompleteJs,
                     delay=data.delay,
                     html= data.html,
-                    pageUrl= data.pageUrl,
+                    pageUrl= $"{data.pageUrl}?url={HttpUtility.UrlEncode(item.url)}",
                     sessionId= data.sessionId,
-                    timeout =data.timeout
-                }.ToJsonString()));
+                    timeout =data.timeout,
+                    name=item.name,
+                };
+                string message = JsonSerializer.Serialize(genQueue);
+                var serviceBusMessage = new ServiceBusMessage(message);
+                serviceBusMessage.ApplicationProperties.Add("name", "BlobRoot");
+                 
+                await azureServiceBus.GetServiceBusClient().SendMessageAsync("dep-genpdf", serviceBusMessage);
             }
-            long expire = (data.delay + data.timeout) * countProcess;
+            long expire = (data.delay + data.timeout) * countProcess+30*60*1000;
             var tiemSpan = TimeSpan.FromMilliseconds(expire);
             await azureRedis.GetRedisClient(8).KeyExpireAsync($"PDFGen:{data.sessionId}", tiemSpan);
-            await azureServiceBus.GetServiceBusClient().SendBatchMessageAsync("genpdf", queueDatas);
-            return new { total = countProcess , add = queueDatas.Count() }; 
+          
+            return ( countProcess , genRedis.Count() ); 
         }
 
     }
@@ -141,11 +152,19 @@ namespace TEAMModelOS.SDK.Models.Service
         /// </summary>
         public string taskName { get; set; }
         /// <summary>
-        /// 艺术评测报告,评测报告,问卷报告,投票报告
+        /// 艺术评测报告Art,评测报告Exam,问卷报告Survey,投票报告Vote,为空 则无法回调更新状态
         /// </summary>
         public string taskType { get; set;}
     }
-    public record PDFData{
+    public class GenPDFSchema 
+    {
+        public List<string> notifyUsers { get; set; } = new List<string>();
+        public string taskName { get; set; }
+        public string taskType { get; set; }
+       
+    }
+
+    public class PDFData{
         /// <summary>
         /// 学生id
         /// </summary>
@@ -154,20 +173,28 @@ namespace TEAMModelOS.SDK.Models.Service
         /// 数据链接
         /// </summary>
         public string url { get; set; }
+        /// <summary>
+        /// 学生名称
+        /// </summary>
+        public string name { get; set; }
     }
 
 
     /// <summary>
     /// redis 存储数据,超时时间设置为status=0 的count   timeout* count+ delay*count
     /// </summary>
-    public record PDFGenRedis 
+    public class PDFGenRedis 
     {
         /// <summary>
         /// 学生id
         /// </summary>
         public string id { get; set; }
         /// <summary>
-        /// 0 未执行,1 执行中,2 执行成功,3 执行失败,4超时,5 取消
+        /// 学生名称
+        /// </summary>
+        public string name { get; set; }
+        /// <summary>
+        /// 0 未执行,1 执行中,2 执行成功,3 执行失败,4超时,5 取消,6 存放异常
         /// </summary>
         public int status { get; set; } = 0;
         /// <summary>
@@ -182,6 +209,14 @@ namespace TEAMModelOS.SDK.Models.Service
         /// 加入时间
         /// </summary>
         public long join     { get; set; }
+        /// <summary>
+        /// 上传成功后的文件
+        /// </summary>
+        public string blob { get; set; }
+        /// <summary>
+        /// 數據的url
+        /// </summary>
+        public string url { get; set; }
     }
 
 
@@ -197,8 +232,13 @@ namespace TEAMModelOS.SDK.Models.Service
 //##  -e PDF_KEEP_DAY=[num] 自动删除num天之前产生的文件目录,默认0: 不删除文件
     //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
     /// </summary>
-    public record PDFGenQueue 
+    public class PDFGenQueue 
     {
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        public string name { get; set; }
+
         /// <summary>
         /// 学生id
         /// </summary>

+ 15 - 289
TEAMModelOS.TEST/Program.cs

@@ -1,11 +1,9 @@
-using Newtonsoft.Json;
+
+
 using System.Security.Policy;
-using System.Text;
 using System.Web;
-using TEAMModelOS.SDK.Extension;
-using TEAMModelOS.SDK.Models;
-using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
-using TEAMModelOS.SDK.Models.Dtos;
+using System;
+using System.IO;
 
 namespace TEAMModelOS.TEST
 {
@@ -13,292 +11,20 @@ namespace TEAMModelOS.TEST
     {
         static void Main(string[] args)
         {
+            var  url=  HttpUtility.UrlDecode("https://teammodelos.blob.core.chinacloudapi.cn/0-public/visitCnt/2345/202406125/55/05.json ");
+           var uri = new Uri(url);
 
-            //
-            var uri = HttpUtility.UrlDecode("https://teammodeltest.blob.core.chinacloudapi.cn/hbcn/art/e9a5ec36-7299-45dc-9517-7457960346c4/report/202106005.pdf");
-            var paths = uri.Split("/art/");
-            if (paths.Length == 2)
-            {
-                var ps = paths[1].Split("/");
-                if (ps.Length == 3)
-                {
-                    Uri uris = new Uri(paths[0]);
-
-                    // 获取URL的Segments属性,这是一个String数组,包含URL中的每个部分
-                    string[] segments = uris.Segments;
-                    string key = $"ArtPDF:{ps[0]}";
-                    // 确保segments数组至少有一个元素
-                    if (segments.Length > 0)
-                    {
-                        // 获取数组中的最后一个元素,即最后一个'/'之后的部分
-                        string lastSegment = segments[segments.Length - 1];
-                        key = $"ArtPDF:{ps[0]}:{lastSegment}";
-                    }
-                }
-            }
-
-        List<Dictionary<string, object>> jsonData = new List<Dictionary<string, object>>
-        {
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711414573130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711414673130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711415673130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711435873130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711436873130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711455873130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711495873130 } },
-            new Dictionary<string, object> { { "user", "A" }, { "time", 1711495973130 } }
-        };
-
-            double totalDuration = GetUserDuration(jsonData);
-            Console.WriteLine("用户使用时长: " + totalDuration + "小时");
-
-            // 作品数据
-            var works = new List<Work>
-        {
-            new Work { WorkId = "1", Subject = "语文" },
-            new Work { WorkId = "2", Subject = "数学" },
-            new Work { WorkId = "3", Subject = "英语" },
-            new Work { WorkId = "4", Subject = "数学" },
-            new Work { WorkId = "5", Subject = "英语" },
-            new Work { WorkId = "6", Subject = "语文" },
-            new Work { WorkId = "7", Subject = "语文" }
-        };
-
-            // 专家数据
-            var experts = new List<ExpertS>
-        {
-            //new ExpertS { ExpertId = "a", Subjects = new List<string> { "语文", "数学" ,"英语"} },
-            new ExpertS { ExpertId = "b", Subjects = new List<string> { "数学" } },
-            //new ExpertS { ExpertId = "c", Subjects = new List<string> { "英语", "英语" } },
-            new ExpertS { ExpertId = "d", Subjects = new List<string> { "数学" } },
-            new ExpertS { ExpertId = "e", Subjects = new List<string> { "英语", "数学" } },
-            //new ExpertS { ExpertId = "f", Subjects = new List<string> { "语文" } }
-        };
-
-            // 分配作品给专家
-            int N = 1; // 指定评审次数
-            var assignments = AssignWorksToExperts(works, experts, N);
-
-            // 输出结果
-            foreach (var assignment in assignments)
-            {
-                Console.WriteLine($"WorkId: {assignment.WorkId}, ExpertId: {assignment.ExpertId}");
-            }
-
-             
-            string path = "C:\\Users\\CrazyIter\\Downloads\\消费清单(2022-2023)\\bill";
-            List<List<string>> inputArray = new List<List<string>>
-            {  
-                new List<string> { "2", "11" },
-                new List<string> { "1", "22" },
-                new List<string> { "1", "11", "111" },
-                new List<string> { "2", "22", "222" },
-                new List<string> { "1", "11" },
-                new List<string> { "1", "11" },
-                new List<string> { "1" },
-                new List<string> { "2", "22", "222" },
-                new List<string> { "2", "22" }  ,
-            };
-
-            // 转换为层级结构
-            List<ClassifiedItem> result = ClassifyHierarchy(inputArray);
-
-            // 输出结果
-            foreach (var item in result)
-            {
-                Console.WriteLine($"id: {item.Id}, count: {item.Count}, pid: {item.Pid}");
-            }
-        }
-
-        static double GetUserDuration(List<Dictionary<string, object>> jsonData)
-        {
-            double totalDuration = 0;
-          //  DateTime lastTime = DateTimeOffset.FromUnixTimeMilliseconds((long)jsonData[0]["time"]).UtcDateTime;
-            long ltime = (long)jsonData[0]["time"];
-            for (int i = 1; i < jsonData.Count; i++)
-            {
-                long ctime = (long)jsonData[i]["time"];
-                //DateTime currentTime = DateTimeOffset.FromUnixTimeMilliseconds((long)jsonData[i]["time"]).UtcDateTime;
-                long  timeDifference = ctime - ltime;
-
-                if (timeDifference < 3600000)
-                {
-                    totalDuration += timeDifference;
-                }
-                else
-                {
-                    totalDuration += 1;
-                }
-
-                ltime = ctime;
-            }
-            return totalDuration;
-        }
-        static List<Assignment> AssignWorksToExperts(List<Work> works, List<ExpertS> experts, int N)
-        {
-            var assignments = new List<Assignment>();
-            foreach (var work in works)
-            {
-                for (int i = 0; i < N; i++)
-                {
-                    var availableExperts = experts
-                        .OrderBy(expert => assignments.Count(a => a.ExpertId == expert.ExpertId))
-                        .Where(expert => expert.Subjects.Contains(work.Subject) && !assignments.Any(a => a.WorkId == work.WorkId && a.ExpertId == expert.ExpertId))
-                        .ToList();
-                    if (availableExperts.Count > 0)
-                    {
-                        var selectedExpert = availableExperts.First();
-                        assignments.Add(new Assignment { WorkId = work.WorkId, ExpertId = selectedExpert.ExpertId });
-                    }
-                    else
-                    {
-                        Console.WriteLine($"No available expert for WorkId {work.WorkId} and subject {work.Subject} in attempt {i + 1}.");
-                        break;
-                    }
-                }
-            }
-
-            return assignments;
-        }
-        static List<ClassifiedItem> ClassifyHierarchy(List<List<string>> inputArray)
-        {
-            Dictionary<string, int> hierarchyCount = new Dictionary<string, int>();
-            List<ClassifiedItem> result = new List<ClassifiedItem>();
-
-            foreach (var list in inputArray)
-            {
-                for (int i = 0; i < list.Count; i++)
-                {
-                    string currentId = list[i];
-                    string parentId = (i > 0) ? list[i - 1] : null;
-
-                    string hierarchyKey = $"{currentId}|{parentId}";
+            // 获取容器名,它是路径的第一个部分
+            string containerName = uri.Segments[1].TrimEnd('/');
 
-                    if (hierarchyCount.ContainsKey(hierarchyKey))
-                    {
-                        hierarchyCount[hierarchyKey]++;
-                    }
-                    else
-                    {
-                        hierarchyCount[hierarchyKey] = 1;
-                    }
-                    var item = result.Find(item => item.Id == currentId && item.Pid == parentId);
-                    if (item== null  )
-                    {
-                        result.Add(new ClassifiedItem
-                        {
-                            Id = currentId,
-                            Pid = parentId,
-                            Count = 0
-                        });
-                    }
-                }
-            }
+            // 获取文件的完整同级目录,这是文件路径中除了文件名和扩展名之外的部分
+            // 由于文件名是路径的最后一个部分,我们可以通过连接除了最后一个部分之外的所有部分来获取目录路径
+            string directoryPath = string.Join("", uri.Segments, 2, uri.Segments.Length - 3);
 
-            foreach (var item in result)
-            {
-                string hierarchyKey = $"{item.Id}|{item.Pid}";
-                item.Count = hierarchyCount.ContainsKey(hierarchyKey) ? hierarchyCount[hierarchyKey] : 0;
-            }
-
-            return result;
-        }
-        class ClassifiedItem
-        {
-            public string Id { get; set; }
-            public int Count { get; set; }
-            public string Pid { get; set; }
+            Console.WriteLine("Container Name: " + containerName);
+            Console.WriteLine("Directory Path: " + directoryPath);
+            string? fileName = Path.GetFileNameWithoutExtension(uri.AbsolutePath);
+            string b = Path.Combine(directoryPath!, $"{fileName}.pdf");
         }
     }
-    class Work
-    {
-        public string WorkId { get; set; }
-        public string Subject { get; set; }
-    }
-
-    class ExpertS
-    {
-        public string ExpertId { get; set; }
-        public List<string> Subjects { get; set; }
-    }
-
-    class Assignment
-    {
-        public string WorkId { get; set; }
-        public string ExpertId { get; set; }
-    }
-    public class LessonBase {
-        public string? id { get; set; }
-        public string? duration { get; set; }
-
-        public string? schoolId { get; set; }
-        public string? schoolName { get; set; }
-
-        public string? scope { get; set; }
-        public string? subjectId { get; set; }
-        public string? subjectName { get; set; }
-        public string? courseId { get; set; }
-        public string? courseName { get; set; }
-        public string? classId { get; set; }
-        public string? className { get; set; }
-        public string? gradeId { get; set; }
-        public string? gradeName { get; set; }
-        public string? teacherId { get; set; }
-        public string? teacherName { get; set; }
-        public long time { get; set; }
-        public int memberCount { get; set; }
-    }
-
-    public class LessonStudent { 
-        public string? studentID { get; set; }
-        public string? groupName { get; set; }
-        public string? groupId { get; set; }
-        /// <summary>
-        /// 1开始,groupIndex=0  表示未分组
-        /// </summary>
-        public int groupIndex { get; set; }
-
-        public int seatID { get; set; }
-        public int studentIndex { get; set; }
-        public string? studentName { get; set; }
-        /// <summary>
-        /// 学生类型 1 醍摩豆id, 2校内账号  ==对应字段 ies_Type   //ID類型 1 tmdid,2 student  本地或動態班級的話會是0
-        /// </summary>
-        public int type  { get; set; }
-        /// <summary>
-        /// 名单id
-        /// </summary>
-        public string? classId { get; set; }
-        /// <summary>
-        ///Uncall,//未點名
-        ///Attended,//已出席
-        ///Absent,//缺席
-        ///DayOff,//請假
-        ///Absent_Sick,//病假
-        ///Absent_Personal,//事假
-        ///Absent_Official,//公假
-        /// </summary>
-        public string? AttendState { get; set; }
-        /// <summary>
-        /// 个人积分
-        /// </summary>
-        public double score { get; set; }
-        /// <summary>
-        /// 小组积分
-        /// </summary>
-        public double groupScore { get; set; }
-        /// <summary>
-        /// 互动积分
-        /// </summary>
-        public double interactScore { get; set; }
-    }
-    // 用于反序列化JSON数据的类
-    public class UsageRecord
-    {
-        [JsonProperty("user")]
-        public string user { get; set; }
-
-        [JsonProperty("time")]
-        public long time { get; set; }
-    }
 }

+ 24 - 2
TEAMModelOS/Controllers/System/GenPDFController.cs

@@ -7,6 +7,10 @@ using TEAMModelOS.SDK;
 using TEAMModelOS.Models;
 using System.Net.Http;
 using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Models.Service;
+using DocumentFormat.OpenXml.Vml;
 
 namespace TEAMModelOS.Controllers
 {
@@ -22,10 +26,11 @@ namespace TEAMModelOS.Controllers
         private readonly IConfiguration _configuration;
         private readonly AzureStorageFactory _azureStorage;
         private readonly AzureRedisFactory _azureRedis;
+        private readonly AzureServiceBusFactory _azureServiceBus ;
         private readonly IPSearcher _ipSearcher;
         private readonly Option _option;
         private readonly Region2LongitudeLatitudeTranslator _longitudeLatitudeTranslator;
-        public GenPDFController(AzureRedisFactory azureRedis, Region2LongitudeLatitudeTranslator longitudeLatitudeTranslator, IHttpClientFactory httpClient, IConfiguration configuration, AzureStorageFactory azureStorage, IPSearcher searcher, DingDing dingDing, IOptionsSnapshot<Option> option)
+        public GenPDFController(AzureServiceBusFactory  azureServiceBus,AzureRedisFactory azureRedis, Region2LongitudeLatitudeTranslator longitudeLatitudeTranslator, IHttpClientFactory httpClient, IConfiguration configuration, AzureStorageFactory azureStorage, IPSearcher searcher, DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _httpClient = httpClient;
             _configuration = configuration;
@@ -35,8 +40,25 @@ namespace TEAMModelOS.Controllers
             _option = option.Value;
             _azureRedis=azureRedis;
             _longitudeLatitudeTranslator = longitudeLatitudeTranslator;
+            _azureServiceBus = azureServiceBus;
+        }
+        /// <summary>
+        /// 艺术评测报告生成
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("art-report")]
+#if !DEBUG
+        [AuthToken(Roles = "teacher,admin")]
+        [Authorize(Roles = "IES")]
+#endif
+        public async Task<IActionResult> ArtReport (GenPDFData request) 
+        {
+            
+            var data = await GenPDFService.AddGenPdfQueue(_azureServiceBus, _azureRedis, request);
+            return Ok(new { total= data.total,add= data .add});
         }
-
     }
 
 }

+ 12 - 1
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -76,7 +76,18 @@ namespace TEAMModelOS.Controllers
         }
 
         //
-
+        /// <summary>
+        /// 移除已经废弃的StuCourse 数据
+        /// </summary>
+        /// <param name="file"></param>
+        /// <param name="periodId"></param>
+        /// <returns></returns>
+        [HttpGet("test-str")]
+        [RequestSizeLimit(102_400_000_00)] //最大10000m左右
+        public async Task<IActionResult> Tes1t() {
+           var a =  _azureStorage.GetBlobServiceClient();
+            return Ok(1);
+        }
 
         /// <summary>
         /// 移除已经废弃的StuCourse 数据

+ 2 - 0
TEAMModelOS/Lang/en-us.json

@@ -35,6 +35,8 @@
   "blob-space-school-notify": [ "There is not enough space left", "Dear {schoolName} administrator {tmdname}, remind you that the remaining space of your school is less than {percent}%, please contact the sales to purchase space, for details, please log in to IES (www.teammodel.net ) to view. Please ignore this notification if the capacity has already been expanded." ],
   "blob-space-private-notify": [ "There is not enough space left", "Hello, {tmdname}, remind you that the remaining space belong to your account {tmdid} is less than {percent}%. Please contact the school administrator to configure or purchase your own space. Please ignore this notification if the capacity has already been expanded." ],
   "school-scale-notify": [ "Student login count has reached the school's scale limit", "Dear {schoolName} administrator {tmdname}, we would like to remind you that the number of student logins at your school today has reached the scale limit: ({scale}). Please contact the sales to purchase more capacity. For more information, please log in to IES (www.teammodel.net). If you have already purchased additional capacity, please disregard this notice." ],
+  "pdf-gen-notify-higher60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,单个报告生成最长耗时{maxTime}秒,单个报告最短耗时{minTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个" ],
+  "pdf-gen-notify-below60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个。以下是生成报告的详情:", "{studentName}(状态:{status},等待:{wait}秒,耗时:{cost}秒,总耗时:{total}秒)。" ],
   "notify-status": {
     "code0": "Token invalid",
     "code1": "Parameter exception",

+ 2 - 0
TEAMModelOS/Lang/zh-cn.json

@@ -39,6 +39,8 @@
   "homework-submit-private-notify": [ "作业提交汇总通知", "{tmdname}您好,你发布的作业:{homeworkName},截至{sendTime},已经有{submitCount}位学生提交,仍有{unsubmitCont}位学生未提交,请查收。" ],
   "grouplist-join-private-notify": [ "名单人员变化通知", "{tmdname}您好,你的个人课程名单:{grouplistName}已有总人数{memberCount},{sendTime}有{joinCount}加入/离开。" ],
   "lesson-create-private-notify": [ "名单人员变化通知", "{tmdname}您好,截至当天{sendTime},您使用HiTeach开课数量为{lessonCount}," ],
+  "pdf-gen-notify-higher60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,单个报告生成最长耗时{maxTime}秒,单个报告最短耗时{minTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个" ],
+  "pdf-gen-notify-below60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个。以下是生成报告的详情:", "{studentName}(状态:{status},等待:{wait}秒,耗时:{cost}秒,总耗时:{total}秒)。" ],
   "notify-status": {
     "code0": "凭据失效",
     "code1": "参数异常",

+ 2 - 0
TEAMModelOS/Lang/zh-tw.json

@@ -35,6 +35,8 @@
   "blob-space-school-notify": [ "剩餘空間不足", "親愛的{schoolName}管理員{tmdname}您好,提醒您貴校剩餘空間不足{percent}%,請聯繫官方客服購買空間,詳情請登入IES(www.teammodel.net)查看,如果已經擴容請忽略此通知。" ],
   "blob-space-private-notify": [ "剩餘空間不足", "親愛的{tmdname}您好,提醒您的帳號{tmdid}授權的剩餘空間不足{percent}%,請聯繫學校管理員配置或是購買個人空間,如果已經擴容請忽略此通知。" ],
   "school-scale-notify": [ "學生登入數已達學校規模數上限", "親愛的{schoolName}管理員{tmdname}您好,提醒您今日貴校學生登入數已達規模數上限:({scale}),請聯繫官方客服購買規模數,詳情請登入IES(www.teammodel.net)查看,如果已經購買請忽略此通知。" ],
+  "pdf-gen-notify-higher60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,单个报告生成最长耗时{maxTime}秒,单个报告最短耗时{minTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个" ],
+  "pdf-gen-notify-below60": [ "PDF报告生成通知", "{tmdname}您好,{taskName}的PDF报告已经生成,总耗时(包含等待和生成时间){totalTime}秒,报告生成平均耗时{avgTime}秒,生成成功{statusOk}个,生成失败{statusFailed}个。以下是生成报告的详情:", "{studentName}(状态:{status},等待:{wait}秒,耗时:{cost}秒,总耗时:{total}秒)。" ],
   "notify-status": {
     "code0": "憑證失效",
     "code1": "參數異常",

ファイルの差分が大きいため隠しています
+ 28 - 28
TEAMModelOS/appsettings.Development.json