using Azure.Storage.Blobs.Models; using IES.ExamLibrary.Models; using IES.ExamServer.Models; using Microsoft.Azure.Cosmos; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK.Models.Cosmos; using TEAMModelOS.SDK.Models.Cosmos.Normal; namespace TEAMModelOS.SDK.Models.Service { public sealed class EvaluationSyncInfoService { /// /// 重新对名单的hash值计算,1因为可能会存在临时将学生加入名单,而没有修改评测信息的情况。 /// /// /// /// /// /// public static async Task<(EvaluationSyncInfo evaluation, bool change,string newGrouplistHash, List members, List groupLists)> CheckEvaluationGroupList(EvaluationSyncInfo evaluationSyncInfo, CoreAPIHttpService _coreAPIHttpService, AzureCosmosFactory _azureCosmos, DingDing _dingDing) { var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, _azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist.Select(x=>x.id).ToList(), evaluationSyncInfo.ownerId); //计算数据的hash值 StringBuilder groupListData = new StringBuilder(); //名单的hash值 var orderList = listInfo.groups.OrderBy(x => x.id); foreach (var item in orderList) { groupListData.Append($"{item.id}-{item.name}"); var orderMembers = item.members.OrderBy(x => x.id); foreach (var member in orderMembers) { groupListData.Append($"{member.id}-{member.name}"); } } bool change = false; string grouplistHash = ShaHashHelper.GetSHA1(groupListData.ToString()); if (!evaluationSyncInfo.grouplistHash .Equals(grouplistHash)) { evaluationSyncInfo.grouplistHash = grouplistHash; change = true; } return (evaluationSyncInfo,change, grouplistHash,listInfo.rmembers, listInfo.groups); } /// /// 活动数据打包 /// /// /// /// /// /// /// public static async Task PackageEvaluation( string id,string scope, string ownerId, string type, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, CoreAPIHttpService _coreAPIHttpService, DingDing _dingDing, IHttpClientFactory _httpClientFactory) { EvaluationSource evaluationSource = new EvaluationSource() { type=type,id=id}; EvaluationSyncInfo evaluationSyncInfo= null; EvaluationClient evaluationClient = null; List evaluationExams = new List(); long? dataTime = 0; long stime = 0; long etime = 0; string? ownerName=string.Empty; string? ownerPicture = string.Empty; string schoolCode = null; if (scope.Equals("school")) { try { School school = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync(ownerId, new PartitionKey("Base")); schoolCode= ownerId; evaluationSource.school = school; ownerName = school.name; ownerPicture = school.picture; //if (!string.IsNullOrWhiteSpace(ownerPicture)) //{ // //学校logo 下载到本地、 // HttpResponseMessage message = await _httpClientFactory.CreateClient().GetAsync(ownerPicture); // if (message.IsSuccessStatusCode) // { // string base64Prefix = "data:image/jpeg;base64,"; // byte[] fileBytes = await message.Content.ReadAsByteArrayAsync(); // ownerPicture =$"{base64Prefix}{Convert.ToBase64String(fileBytes)}"; // } //} } catch (Exception ex) { await _dingDing.SendBotMsg($"打包评测数据,获取学校信息报错,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組); return null; } } else { try { Teacher teacher = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync(ownerId, new PartitionKey("Base")); evaluationSource.teacher = teacher; ownerName = teacher.name; ownerPicture = teacher.picture; //if (!string.IsNullOrWhiteSpace(ownerPicture)) //{ // //学校logo 下载到本地、 // HttpResponseMessage message = await _httpClientFactory.CreateClient().GetAsync(ownerPicture); // if (message.IsSuccessStatusCode) // { // string base64Prefix = "data:image/jpeg;base64,"; // byte[] fileBytes = await message.Content.ReadAsByteArrayAsync(); // ownerPicture =$"{base64Prefix}{Convert.ToBase64String(fileBytes)}"; // } //} } catch (Exception ex) { await _dingDing.SendBotMsg($"打包评测数据,获取教师信息报错,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組); return null; } } var responseEvaluationSyncInfo = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemStreamAsync(id, new PartitionKey("EvaluationSyncInfo")); if (responseEvaluationSyncInfo.IsSuccessStatusCode) { evaluationSyncInfo= JsonDocument.Parse(responseEvaluationSyncInfo.Content).RootElement.Deserialize(); } else { evaluationSyncInfo=new EvaluationSyncInfo { id = id, scope = scope, type = type, pk="EvaluationSyncInfo", code="EvaluationSyncInfo", ownerId = ownerId, ownerPicture = ownerPicture, ownerName = ownerName, }; } switch (true) { case bool when (type == "Exam"): { string code = $"Exam-{ownerId}"; var response = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(id, new PartitionKey(code)); if (response.IsSuccessStatusCode) { ExamInfo exam= JsonDocument.Parse(response.Content).RootElement.Deserialize(); dataTime = exam._ts*1000; evaluationSyncInfo.name=exam.name; evaluationSyncInfo.subjects = exam.subjects?.Select(x=>new IES.ExamServer.Models.SubjectExam { subjectId=x.id,subjectName=x.name,examId=id,examName=exam.name}).ToList(); evaluationSyncInfo.dataTime= DateTimeOffset.Now.ToUnixTimeMilliseconds(); evaluationSyncInfo.scode=exam.code; evaluationSyncInfo.owner=exam.owner; stime=exam.startTime; etime=exam.endTime; HashSet grouplist = new HashSet(); if (exam.classes.IsNotEmpty()) { exam.classes.ForEach(x => { grouplist.Add(x); }); } if (exam.stuLists.IsNotEmpty()) { exam.stuLists.ForEach(x => { grouplist.Add(x); }); } evaluationSyncInfo.grouplist=grouplist.Select(x=> new EvaluationGroupListDto { id=x}).ToList() ; evaluationSyncInfo.paperCount=exam.papers.IsNotEmpty()? exam.papers.Count():0; foreach (var group in exam.papers.GroupBy(x=>x.subjectId).Select(x=>new { key = x.Key,list= x.ToList()})) { var subject= exam.subjects.Find(x => x.id.Equals(group.key)); if (subject!=null) { evaluationSyncInfo.subjects.Add(new IES.ExamServer.Models.SubjectExam { subjectId=subject.id, subjectName=subject.name, examId=id, examName=exam.name, papers= group.list.Select(x=>new SubjectExamPaper {paperId= x.id,paperName=x.name,limitTime=x.time, questionCount=x.answers.IsNotEmpty()?x.answers.Count():0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList(), } ); EvaluationExam evaluationExam = new EvaluationExam() { examId=exam.id, evaluationId=evaluationSyncInfo.id, examName=evaluationSyncInfo.name, subjectId=subject.id, subjectName=subject.name, classes= evaluationSyncInfo.grouplist, owner=exam.owner, scope=scope, stime=stime, etime=etime, papers= group.list.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, limitTime=x.time, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point,knowledge=x.knowledge,type=x.type,field=x.field }).ToList(), }; evaluationExams.Add(evaluationExam); } } evaluationSource.exam=exam; } break; } case bool when (type == "Art"): { string code = $"Art-{ownerId}"; var response = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(id, new PartitionKey(code)); if (response.IsSuccessStatusCode) { ArtEvaluation art = JsonDocument.Parse(response.Content).RootElement.Deserialize(); // evaluationSyncInfo.subjects = art.subjects?.Select(x => new IES.ExamServer.Models.SubjectExam { id=x.id, name=x.name, examId=id }).ToList(); evaluationSyncInfo.name = art.name; evaluationSyncInfo.pid= art.pId; evaluationSyncInfo.scode=art.code; evaluationSyncInfo.dataTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); evaluationSyncInfo.owner=art.owner; HashSet grouplist = new HashSet(); if (art.classes.IsNotEmpty()) { art.classes.ForEach(x => { grouplist.Add(x); }); } if (art.stuLists.IsNotEmpty()) { art.stuLists.ForEach(x => { grouplist.Add(x); }); } if (art.tchLists.IsNotEmpty()) { art.tchLists.ForEach(x => { grouplist.Add(x); }); } evaluationSyncInfo.grouplist=grouplist.Select(x => new EvaluationGroupListDto { id = x }).ToList(); evaluationSyncInfo.examDeadline=art.examDeadline; evaluationSource.art=art; dataTime= art._ts*1000; stime=art.startTime; etime=art.endTime; var quota_21 = art.settings.Find(x => x.id.Equals("quota_21")); foreach (var item in quota_21.task) { if (!string.IsNullOrWhiteSpace(item.acId)) { // item.isOrder=>disorder var subject = art.subjects.Find(x => x.id.Equals(item.subject)); var examResponse= await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(item.acId, new PartitionKey($"Exam-{ownerId}")); if (examResponse.IsSuccessStatusCode) { ExamInfo exam = JsonDocument.Parse(examResponse.Content).RootElement.Deserialize() ; var papers = exam.papers.FindAll(x => x.subjectId.Equals(item.subject)); var subjectSync = evaluationSyncInfo.subjects.Find(x => x.examId.Equals(item.acId) && x.subjectId.Equals(item.subject)); if (subjectSync!=null) { subjectSync.papers= papers?.Select(x => new SubjectExamPaper { paperId=x.id, limitTime=x.time, paperName=x.name, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList(); subjectSync.subjectName=subject.name; } else { evaluationSyncInfo.subjects.Add(new IES.ExamServer.Models.SubjectExam { subjectId=item.subject, subjectName=subject.name, examId=item.acId, examName=exam.name, disorder=item.isOrder.HasValue ? item.isOrder.Value : 0, papers = papers?.Select(x => new SubjectExamPaper { paperId=x.id, paperName=x.name, limitTime=x.time, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList() }); } dataTime= dataTime new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, limitTime=x.time, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point, knowledge=x.knowledge, type=x.type, field=x.field }).ToList(), }; evaluationExams.Add(evaluationExam); } } } var quota_22 = art.settings.Find(x => x.id.Equals("quota_22")); if (quota_22!=null) { foreach (var item in quota_22.task) { //获取基本技能(智音音乐曲目) if (item.subject.Equals("subject_music")) { string AIMuiscId = string.Empty; if (!string.IsNullOrWhiteSpace(art.pId)) { AIMuiscId=art.pId; } else { AIMuiscId=art.id; } var artMusicResponse = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(AIMuiscId, new PartitionKey("ArtMusic")); if (artMusicResponse.IsSuccessStatusCode) { ArtMusic artMusic = JsonDocument.Parse(artMusicResponse.Content).RootElement.Deserialize() ; if (artMusic!=null) { evaluationSyncInfo.music= new MusicAI() { taskId=item.acId, examId= art.id, examName= art.name, pid=art.pId, questionId=artMusic.questionId, questionName=artMusic.questionName, mustSong=artMusic.mustSong?.Select(x=>new AISong { songId=x.songId,songName=x.songName}).ToList(), optionSong=artMusic.optionSong?.Select(x => new AISong { songId=x.songId, songName=x.songName }).ToList(), }; } } } } } } } break; default: break; } if (evaluationSyncInfo.subjects.IsNotEmpty() && evaluationSyncInfo.grouplist.IsNotEmpty()) { long blobTime =-1; long blobSize = 0; long blobCount = 0; if (string.IsNullOrWhiteSpace(evaluationSyncInfo.shortCode)) { evaluationSyncInfo.shortCode = $"{MurmurHash3.Hash32(evaluationSyncInfo.id)}"; } if (string.IsNullOrWhiteSpace(evaluationSyncInfo.openCode)) { StringBuilder builder = new StringBuilder(); for (var i = 0; i < 6; i++) { var chars = ExamConstant.AZ_19NI1O0[Random.Shared.Next(0, ExamConstant.AZ_19NI1O0.Length)]; builder.Append(chars); } evaluationSyncInfo.openCode = builder.ToString(); } var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist.Select(x=>x.id).ToList(), schoolCode); foreach (var item in evaluationSyncInfo.grouplist) { item.name=listInfo.groups.Find(x => x.id.Equals(item.id))?.name; } evaluationSyncInfo.studentCount = listInfo.rmembers.Count(); evaluationSyncInfo.paperCount =evaluationSyncInfo.subjects.SelectMany(x => x.papers).Count(); var client = azureStorage.GetBlobContainerClient(ownerId); foreach (var subject in evaluationSyncInfo.subjects) { var evaluationExam = evaluationExams.FindAll(x => x.subjectId.Equals(subject.subjectId)).FirstOrDefault(); foreach (var paper in subject.papers) { EvaluationPaper evaluationPaper = null; List blobs = new List(); try { await foreach (BlobItem blobItem in client.GetBlobsAsync(BlobTraits.None, BlobStates.None, !string.IsNullOrWhiteSpace(paper.blob)&& paper.blob.StartsWith("/")?paper.blob.Substring(1):paper.blob )) { var lastModified = blobItem.Properties.LastModified; if (lastModified.HasValue) { lastModified.Value.ToUnixTimeMilliseconds(); } var hash = blobItem.Properties.ContentHash; var path = blobItem.Name; var ext = Path.GetExtension(path); string fileName = Path.GetFileName(path); if (ext.ToUpper().Equals(ext)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); fileName= $"{fileNameWithoutExtension!}_1{ext}"; } var size = blobItem.Properties.ContentLength; blobs.Add(new BlobHashInfo { hash = Md5Hash.GetbyteToString(hash), last = lastModified.HasValue ? lastModified.Value.ToUnixTimeMilliseconds() : 0 , path = path, size = size.HasValue ? size.Value : 0, local=$"package/{evaluationSyncInfo.id}/papers/{paper.paperId}/{fileName}" }); }; evaluationPaper = evaluationExam.papers.Find(x => x.paperId.Equals(paper.paperId)); evaluationPaper.blobs=blobs; } catch(Exception ex ) { await _dingDing.SendBotMsg($"数据打包异常,试卷检测异常,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組); } long lastTime = blobs.Max(x => x.last); blobTime= lastTime>blobTime?lastTime:blobTime; if (blobs.IsNotEmpty()) { blobSize+=blobs.Sum(x => x.size); blobCount+=blobs.Count; var order = blobs.OrderBy(x => $"{x.path}-{x.hash}-{x.size}-{x.last}"); string blobStr = string.Join(",", order.Select(x=> $"{x.path}-{x.hash}-{x.size}-{x.last}")); // 计算hash,校准路径,文件hash,文件大小,最后修改时间 string paperHash = ShaHashHelper.GetSHA1(blobStr); paper.paperHash= paperHash; evaluationPaper.paperHash= paperHash; } } } evaluationSyncInfo.blobTime = blobTime> evaluationSyncInfo.blobTime ? blobTime : evaluationSyncInfo.blobTime; evaluationSyncInfo.blobSize = blobSize; evaluationSyncInfo.blobCount = blobCount; evaluationSyncInfo.blobLastHash = evaluationSyncInfo.blobHash; evaluationSyncInfo.blobHash = ShaHashHelper.GetSHA1(string.Join("-", evaluationSyncInfo.subjects.SelectMany(x => x.papers).Select(x => x.paperHash).OrderBy(x=>x))); evaluationSyncInfo.dataTime = dataTime.Value; evaluationSource.updateTime = dataTime.Value; var groupList = new { members = listInfo.rmembers, groupList = listInfo.groups }; { //计算数据的hash值 StringBuilder groupListData = new StringBuilder(); //名单的hash值 var orderList = listInfo.groups.OrderBy(x => x.id); foreach (var item in orderList) { groupListData.Append($"{item.id}-{item.name}"); var orderMembers = item.members.OrderBy(x => x.id); foreach (var member in orderMembers) { groupListData.Append($"{member.id}-{member.name}"); } } evaluationSyncInfo.grouplistHash= ShaHashHelper.GetSHA1(groupListData.ToString()); var order= evaluationSyncInfo.subjects.OrderBy(x => x.subjectId); StringBuilder dataStr = new StringBuilder(); dataStr.Append(evaluationSyncInfo.id); dataStr.Append(evaluationSyncInfo.name); dataStr.Append(evaluationSyncInfo.type); dataStr.Append(evaluationSyncInfo.owner); dataStr.Append(evaluationSyncInfo.ownerId); dataStr.Append(evaluationSyncInfo.scode); dataStr.Append(evaluationSyncInfo.scope); dataStr.Append(evaluationSyncInfo.grouplistHash); dataStr.Append(evaluationSyncInfo.blobHash); dataStr.Append(evaluationSyncInfo.shortCode); dataStr.Append(evaluationSyncInfo.openCode); dataStr.Append($"{stime}{etime}"); dataStr.Append(string.Join("", order.Select(x=>x.subjectId).OrderBy(x=>x))); if (evaluationSyncInfo.music!=null) { dataStr.Append($"{evaluationSyncInfo.music.questionId}{string.Join("", evaluationSyncInfo.music.mustSong?.Select(x=>x.songId).OrderBy(x=>x))}{string.Join("",evaluationSyncInfo.music.optionSong?.Select(x=>x.songId).OrderBy(x=>x))}"); } //计算dataHash evaluationSyncInfo.dataHash = ShaHashHelper.GetSHA1(dataStr.ToString()); } evaluationClient= new EvaluationClient { id = evaluationSyncInfo.id, pid = evaluationSyncInfo.pid, name = evaluationSyncInfo.name, type = evaluationSyncInfo.type, owner = evaluationSyncInfo.owner, scode = evaluationSyncInfo.scode, scope = evaluationSyncInfo.scope, subjects = evaluationSyncInfo.subjects, dataTime = evaluationSyncInfo.dataTime, dataSize = evaluationSyncInfo.dataSize, blobTime = evaluationSyncInfo.blobTime, blobSize = evaluationSyncInfo.blobSize, blobCount = evaluationSyncInfo.blobCount, blobHash = evaluationSyncInfo.blobHash, blobLastHash = evaluationSyncInfo.blobLastHash, //webviewCount = evaluationSyncInfo.webviewCount, //webviewPath = evaluationSyncInfo.webviewPath, //webviewSize = evaluationSyncInfo.webviewSize, //webviewTime = evaluationSyncInfo.webviewTime, studentCount = evaluationSyncInfo.studentCount, paperCount = evaluationSyncInfo.paperCount, grouplist = evaluationSyncInfo.grouplist, shortCode = evaluationSyncInfo.shortCode, openCode = evaluationSyncInfo.openCode, stime=stime, etime=etime, dataHash = evaluationSyncInfo.dataHash, grouplistHash = evaluationSyncInfo.grouplistHash, ownerId = evaluationSyncInfo.ownerId, ownerPicture =evaluationSyncInfo.ownerPicture, ownerName = evaluationSyncInfo.ownerName, music = evaluationSyncInfo.music, reanswer = evaluationSyncInfo.reanswer, examDeadline = evaluationSyncInfo.examDeadline, //password = evaluationSyncInfo.password, //recordUrl = evaluationSyncInfo.recordUrl }; if (!string.IsNullOrWhiteSpace(ownerPicture)) { //教师头像或学校logo 下载转为Base64 HttpResponseMessage message = await _httpClientFactory.CreateClient().GetAsync(ownerPicture); if (message.IsSuccessStatusCode) { string base64Prefix = "data:image/jpeg;base64,"; byte[] fileBytes = await message.Content.ReadAsByteArrayAsync(); ownerPicture =$"{base64Prefix}{Convert.ToBase64String(fileBytes)}"; evaluationClient.ownerPicture = ownerPicture; } } long dataSize = 0; string sourceJson = evaluationSource.ToJsonString(); dataSize+= Encoding.UTF8.GetByteCount(sourceJson); string groupListJson = groupList.ToJsonString(); dataSize+= Encoding.UTF8.GetByteCount(groupListJson); string evaluationJson = new { evaluationClient, evaluationExams }.ToJsonString().ToJsonString(); dataSize+= Encoding.UTF8.GetByteCount(evaluationJson); string evaluationSyncInfoSJson = evaluationSyncInfo.ToJsonString(); dataSize+= Encoding.UTF8.GetByteCount(evaluationSyncInfoSJson); evaluationClient.dataSize = dataSize; //重组blob路径,使之适配局域网端读取。 //evaluationClient.subjects.ForEach(x => { // x.papers.ForEach(y => { // y.blob=$"package/{evaluationClient.id}/papers/{y.paperId}"; // }); //}); //evaluationExams.ForEach(x => { // x.papers.ForEach(y => { // y.blob=$"package/{evaluationClient.id}/papers/{y.paperId}"; // }); //}); //evaluationSyncInfo.subjects.ForEach(x => { // x.papers.ForEach(y => { // y.blob=$"package/{evaluationClient.id}/papers/{y.paperId}"; // }); //}); await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(sourceJson, $"package/{id}/data", "source.json"); await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(groupListJson, $"package/{id}/data", "grouplist.json"); await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"package/{id}/data", "evaluation.json"); evaluationSyncInfo.dataSize = dataSize; await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(evaluationSyncInfo, new PartitionKey("EvaluationSyncInfo")); // await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(evaluationSyncInfo.ToJsonString(), $"package/{id}", "syncinfo.json"); } return evaluationSyncInfo; } } }