using Azure.Messaging.ServiceBus; using Azure.Storage.Blobs.Models; using Azure; using Microsoft.Azure.Cosmos; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Threading.Tasks; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK.Models.Service.BI; using TEAMModelOS.SDK.Services; using static TEAMModelOS.SDK.Models.JointEventGroupBase; using Azure.Core; using Microsoft.Extensions.Configuration; namespace TEAMModelOS.SDK.Models.Service { public static class JointService { //取得JointExam生成Exam public static async Task GenerateExamFromJointExamAsync(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam) { try { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); //取得JointExam List classes = new List(); List stuLists = new List(); //取得JointCourse ※examType == "custom" 之後再處理 List jointCourses = new List(); if (!jointExam.examType.Equals("custom")) { string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}'"; await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql(queryText: jointCourseSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"JointCourse") })) { jointCourses.Add(item); } } //評量資料生成 ExamInfo actExamInfo ※一個課程一個Exam List examList = new List(); foreach (JointEventGroupDb jointCourse in jointCourses) { string actExamCreatorId = jointCourse.creatorId; //個人課程 if(jointCourse.scope.Equals("private")) { foreach (JointEventGroupCourse jointExamGroupCourse in jointCourse.courseLists) { string actExamCourseId = jointExamGroupCourse.courseId; string actExamCourseName = jointExamGroupCourse.courseName; //評量資料生成 ExamInfo actExamInfo = new ExamInfo(); ///取得已生成的Exam ※ string examSql = $"SELECT DISTINCT c.id, c.source, c.name, c.jointExamId, c.subjects, c.stuLists, c.targets, c.papers, c.year, c.startTime, c.endTime FROM c JOIN s IN c.subjects WHERE c.jointExamId = '{jointExam.id}' AND s.id = '{actExamCourseId}'"; await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql(queryText: examSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{actExamCreatorId}") })) { actExamInfo = item; } ///尚無評量資料 if (string.IsNullOrWhiteSpace(actExamInfo.id)) { actExamInfo.code = $"Exam-{actExamCreatorId}"; actExamInfo.owner = "teacher"; actExamInfo.scope = jointCourse.scope; actExamInfo.creatorId = actExamCreatorId; actExamInfo.id = Guid.NewGuid().ToString(); } actExamInfo.source = jointExam.source; actExamInfo.name = jointExam.name; actExamInfo.jointExamId = jointExam.id; actExamInfo.subjects = new List() { new ExamSubject() { id = actExamCourseId, name = actExamCourseName, classCount = jointExamGroupCourse.groupLists.Count } }; ///評量stuLists foreach (JointEventGroupCourseGroup actGroup in jointExamGroupCourse.groupLists) { if(!actExamInfo.stuLists.Contains(actGroup.id)) { actExamInfo.stuLists.Add(actGroup.id); } List targetRow = new List() { actExamCourseId, actGroup.id }; var targetRowJson = JsonSerializer.SerializeToElement(targetRow); if(!actExamInfo.targets.Contains(targetRowJson)) { actExamInfo.targets.Add(targetRowJson); } } ///試卷 actExamInfo.papers = Newtonsoft.Json.JsonConvert.DeserializeObject>(Newtonsoft.Json.JsonConvert.SerializeObject(jointExam.papers)); ///時間 actExamInfo.year = DateTimeOffset.UtcNow.Year; actExamInfo.startTime = jointExam.startTime; actExamInfo.endTime = jointExam.endTime; examList.Add(actExamInfo); } } //[待做] 學校班級 } //生成評量 if (examList.Count > 0) { foreach (ExamInfo exam in examList) { await GenerateExam(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, jointExam, exam); } } } catch (Exception) { } } //生成評量(單) private static async Task GenerateExam(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam, ExamInfo exam) { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); string Result = string.Empty; exam.createTime = now; if (exam.startTime <= 0) exam.startTime = now; List<(string pId, List gid)> ps = new(); var group = exam.groupLists; if (group.Count > 0) { foreach (var keys in group) { foreach (KeyValuePair> pp in keys) { ps.Add((pp.Key, pp.Value)); } } } List classes = ExamService.getClasses(exam.classes, exam.stuLists); (List tchList, List classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, exam.school, ps); exam.stuCount = tchList.Count; string mode = string.Empty; ResponseMessage response = null; if (string.IsNullOrEmpty(exam.id)) { mode = "add"; } else { response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(exam.id, new PartitionKey($"{exam.code}")); if (response.StatusCode == System.Net.HttpStatusCode.OK) mode = "upd"; else mode = "add"; } //DB操作 if (mode.Equals("add")) //新建 { if (string.IsNullOrWhiteSpace(exam.id)) exam.id = Guid.NewGuid().ToString(); exam.progress = (exam.startTime > now) ? "pending" : "going"; var messageBlob = new ServiceBusMessage(); if (exam.scope.Equals("school") && !string.IsNullOrWhiteSpace(exam.school)) { exam.size = await _azureStorage.GetBlobContainerClient(exam.school).GetBlobsSize($"exam/{exam.id}"); await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "insert", root = $"exam", name = exam.school }, _serviceBus, _configuration, _azureRedis); } else { exam.size = await _azureStorage.GetBlobContainerClient(exam.creatorId).GetBlobsSize($"exam/{exam.id}"); await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "insert", root = $"exam", name = exam.creatorId }, _serviceBus, _configuration, _azureRedis); } int n = 0; List sheetIds = new List(); foreach (PaperSimple simple in exam.papers) { simple.blob = $"/exam/{exam.id}/paper/{exam.subjects[n].id}"; n++; simple.sheet = null; } exam = await client.GetContainer(Constant.TEAMModelOS, "Common").CreateItemAsync(exam, new PartitionKey($"{exam.code}")); await BIStats.SetTypeAddStats(client, _dingDing, exam.school, "Exam", 1);//BI统计增/减量 } else if (response != null) //更新 { using var json = await JsonDocument.ParseAsync(response.Content); ExamInfo info = json.ToObject(); if (info.progress.Equals("going")) { Result = "活动正在进行中,无法修改"; } var messageBlob = new ServiceBusMessage(); if (exam.scope.Equals("school") && !string.IsNullOrWhiteSpace(exam.school)) { exam.size = await _azureStorage.GetBlobContainerClient(exam.school).GetBlobsSize($"exam/{exam.id}"); await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = $"exam", name = exam.school }, _serviceBus, _configuration, _azureRedis); } else { exam.size = await _azureStorage.GetBlobContainerClient(exam.creatorId).GetBlobsSize($"exam/{exam.id}"); await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = $"exam", name = exam.creatorId }, _serviceBus, _configuration, _azureRedis); } exam.progress = info.progress; int n = 0; List sheetIds = new List(); foreach (PaperSimple simple in exam.papers) { if (!string.IsNullOrEmpty(simple.subjectId)) { simple.blob = $"/exam/{exam.id}/paper/{simple.subjectId}/{simple.id}"; } else { simple.blob = $"/exam/{exam.id}/paper/{exam.subjects[n].id}"; n++; } simple.sheet = null; } exam = await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(exam, exam.id, new PartitionKey($"{exam.code}")); } //Blob操作 ※取得試卷源(blob)、複製到評測紀錄下 ///試卷源字典 List> sourcePaperInfo = new List>(); foreach (PaperSimple paperInfo in jointExam.papers) { string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob; paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath; sourcePaperInfo.Add(new Dictionary() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "itemcount", paperInfo.point.Count.ToString() } }); } bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤 //Blob拷貝程序 int paperIndex = 0; foreach (Dictionary sourcePaperInfoDic in sourcePaperInfo) { //拷貝源:Container => jointExam.creatorId Path:papers.blob //拷貝對象:Container => exam.creatorId, Path:exam/{exam.id}/paper/{exam.subjects[paperIndex].id}/ string targetScope = exam.scope; //評測對象 school:校本班級 private:私人課程 var sourceBlobContainer = _azureStorage.GetBlobContainerClient(jointExam.creatorId); //統測活動來源一定是個人 var blobPrivateContainer = (targetScope.Equals("school")) ? _azureStorage.GetBlobContainerClient(exam.school) : _azureStorage.GetBlobContainerClient(exam.creatorId); string sourceBlobPath = sourcePaperInfoDic["blob"]; string subjectId = exam.subjects[paperIndex].id; string destBlobPath = $"exam/{exam.id}/paper/{subjectId}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/ Pageable sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath); if (sourceBlobs.Count() > 0) { foreach (var blob in sourceBlobs) { var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name); if (sourceFileBlob.Exists()) { var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri; string fileName = blob.Name.Replace(sourceBlobPath, ""); string destBlobFilePath = $"{destBlobPath}{fileName}"; await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri); } else { paperDataCopyErrFlg = true; } } } paperIndex++; } return Result; } } }