Przeglądaj źródła

Merge branch 'develop5.0-tmd' of http://106.12.23.251:10000/TEAMMODEL/TEAMModelOS into develop5.0-tmd

OnePsycho 3 lat temu
rodzic
commit
bb0a4bbff8

+ 6 - 11
TEAMModelFunction/TriggerSurvey.cs

@@ -39,7 +39,7 @@ namespace TEAMModelFunction
                     adcode = $"Activity-{tdata.school}";
                     blobcntr = tdata.school;
                 }
-                else if (tdata.scope .Equals("private"))
+                else 
                 {
                     adcode = $"Activity-{tdata.creatorId}";
                     blobcntr = tdata.creatorId;
@@ -200,7 +200,7 @@ namespace TEAMModelFunction
                             List<QuestionRecord> questionRecords = new List<QuestionRecord>();
                             //结算每道题的答题情况
                             var ContainerClient = _azureStorage.GetBlobContainerClient(blobcntr);
-                            //List<Task<string>> tasks = new List<Task<string>>();
+                            List<Task<string>> tasks = new List<Task<string>>();
                             //获取
                             try
                             {
@@ -255,9 +255,9 @@ namespace TEAMModelFunction
                                         }
                                     }
                                     questionRecords.Add(question);
-                                    //tasks.Add(  _azureStorage.UploadFileByContainer(blobcntr, question.ToJsonString(), "survey", url));
+                                    tasks.Add(  _azureStorage.UploadFileByContainer(blobcntr, question.ToJsonString(), "survey", url));
                                 }
-                                // await Task.WhenAll(tasks);
+                                 await Task.WhenAll(tasks);
                             }
                             catch (Exception ex)
                             {
@@ -273,8 +273,8 @@ namespace TEAMModelFunction
                             }
                             else
                             {
-                                //_azureRedis.GetRedisClient(8).KeyDelete($"Survey:Record:{survey.id}");
-                                //_azureRedis.GetRedisClient(8).KeyDelete($"Survey:Submit:{survey.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Record:{survey.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Submit:{survey.id}");
                                 break;
                             }
 
@@ -319,9 +319,4 @@ namespace TEAMModelFunction
             }
         }
      **/
-    public class QuestionRecord { 
-        public int index { get; set; }
-        public Dictionary<string, HashSet<string>> opt { get; set; } = new Dictionary<string, HashSet<string>>();
-        public Dictionary<string, string> other { get; set; } = new Dictionary<string, string>();
-    }
 }

+ 3 - 3
TEAMModelFunction/TriggerVote.cs

@@ -37,7 +37,7 @@ namespace TEAMModelFunction
                     adcode = $"Activity-{tdata.school}";
                     blobcntr = tdata.school;
                 }
-                else if (tdata.scope.Equals("private"))
+                else  
                 {
                     adcode = $"Activity-{tdata.creatorId}";
                     blobcntr = tdata.creatorId;
@@ -224,8 +224,8 @@ namespace TEAMModelFunction
                             else
                             {
                                 //异动,且已经有结算记录则不必再继续。
-                                //_azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}");
-                                //_azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}");
                                 break;
                             }
                             await Task.WhenAll(tasks);

+ 13 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/QuestionRecord.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models
+{
+    public class QuestionRecord
+    {
+        public int index { get; set; }
+        public Dictionary<string, HashSet<string>> opt { get; set; } = new Dictionary<string, HashSet<string>>();
+        public Dictionary<string, string> other { get; set; } = new Dictionary<string, string>();
+    }
+}

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/VoteRecord.cs

@@ -2,7 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Text;
 
-namespace TEAMModelOS.SDK.Models.Cosmos.Common.Inner
+namespace TEAMModelOS.SDK.Models
 {
     public class VoteRecord
     {

+ 131 - 73
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue

@@ -204,75 +204,95 @@
                                     ({{item.code}})<div style="margin-left:10px" v-html="item.value"></div>
                                 </div>
                                 <!-- 作答结果 -->
-                                <div class="TitleRec1"><span style="margin:5px;color:#1472c7">{{$t("studentWeb.exam.report.ansRes")}}:</span></div>
-                                <br />
-                                <div v-if="ansData[index]" style="margin-left:10px" v-html="ansData[index].length > 0 ? ansData[index].join(' ') : $t('studentWeb.exam.report.noAns')"></div>
+                                <div>
+                                    <div class="TitleRec1">
+                                        <span style="margin:5px;color:#1472c7">{{ $t("studentWeb.exam.report.ansRes") }}:</span>
+                                    </div>
+                                    <div v-if="ansData[index]" style="margin-left:10px" v-html="ansData[index].length > 0 ? ansData[index].join(' ') : $t('studentWeb.exam.report.noAns')"></div>
+                                </div>
+                                <!-- 批注 -->
+                                <div>
+                                    <div class="TitleRec2">
+                                        <span style="margin:5px;">{{ $t("studentWeb.exam.report.mark") }}:</span>
+                                    </div>
+                                    <div v-if="markData[index] && markData[index].length" style="margin-left:10px;">
+                                        <img v-for="(mark, mIn) in markData[index]" :key="mIn" :src="mark.imgUrl" alt="">
+                                    </div>
+                                    <div v-else style="margin-left:10px;">{{ $t("studentWeb.exam.report.noMark") }}</div>
+                                </div>
                             </div>
                         </div>
                         <!-- 参考答案、解析 -->
                         <div class="rightAnalys">
-                            <div class="TitleRec2"><span style="margin-left:5px">{{$t("studentWeb.exam.report.testAns")}}:</span></div>
-                            <br />
-                            <div style="display: flex">
-                                <div v-for="(item,index) in question.answer" :key="index" v-html="item" style="margin-left:10px;"></div>
+                            <div>
+                                <div class="TitleRec2">
+                                    <span style="margin-left:5px">{{$t("studentWeb.exam.report.testAns")}}:</span>
+                                </div>
+                                <div style="display: flex">
+                                    <div v-for="(item,index) in question.answer" :key="index" v-html="item" style="margin-left:10px;"></div>
+                                </div>
                             </div>
-                            <div class="TitleRec2"><span style="margin-left: 5px">{{$t("studentWeb.exam.report.testAnalyse")}}:</span></div>
-                            <br />
-                            <div style="margin-left: 10px;" v-html="question.explain != '' ?  question.explain : $t('studentWeb.exam.report.noAnalyse') "></div>
-                            <div v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score" class="TitleRec2"><span style="margin-left:5px">{{$t("studentWeb.exam.report.repairSource")}}:</span></div>
-                            <br />
-                            <div style="margin-left: 10px; display: flex" v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score">
-                                <span v-if="question.repair.length == 0">{{$t("studentWeb.exam.report.noSource")}}</span>
-                                <div v-if="question.repair && question.repair.length > 0" class="repair-box">
-                                    <Collapse style="width:85%" accordion @on-change="getSource(question.repair,question)">
-                                        <!-- 网络资源 -->
-                                        <Panel name="1">
-                                            {{$t("studentWeb.exam.report.linkSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item,normalIndex) in repairSource.normal" :key="normalIndex">
-                                                        <span style="margin-right: 10px;" v-show="item.blobUrl">
-                                                            <Icon color="#0066FF" custom="iconfont icon-share_link" size="20" />
-                                                        </span><a :href="item.blobUrl" target="_blank">{{item.name}}</a>
-                                                    </ListItem>
-                                                    <ListItem v-show="repairSource.normal.length == 0">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                        <!-- 文件资源 -->
-                                        <Panel name="2">
-                                            {{$t("studentWeb.exam.report.fileSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item,fileIndex) in repairSource.file" :key="fileIndex">
-                                                        <div style="width:100%">
-                                                            <span style="margin-right:10px;">
-                                                                <Icon v-if="item.fileType == 'zip'" custom="iconfont icon-zip" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" color="#8199AF" custom="iconfont icon-zip" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'pdf'" color="#FF6464" custom="iconfont icon-pdf" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'ppt'|| item.fileType == 'pptx'" color="#FF8976" custom="iconfont icon-ppt" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp3'" color="#FF5562" custom="iconfont icon-mp3" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp4'" color="#8E4C9E" custom="iconfont icon-video1" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" custom="iconfont icon-video1" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'doc'||item.fileType =='docx'" color="#6CCBFF" custom="iconfont icon-word" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'csv'||item.fileType =='xlsx'||item.fileType =='xls'" color="#25C273" custom="iconfont icon-xlsx" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'jpg'||item.fileType =='png'||item.fileType =='jpeg'" color="#30D1CA" custom="iconfont icon-jpg" size="20" />
-                                                                <Icon v-else custom="iconfont icon-V" size="20" />
-                                                            </span><a @click="getItemData(item)">{{item.name}}</a>
-                                                            <span style="display:block;float:right;cursor:pointer">
-                                                                <Icon type="md-download" size="18" @click="downloadFile(item)" />
-                                                            </span>
-                                                        </div>
-                                                    </ListItem>
-                                                    <ListItem v-show="repairSource.file.length == 0">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                    </Collapse>
+                            <div>
+                                <div class="TitleRec2">
+                                    <span style="margin-left: 5px">{{$t("studentWeb.exam.report.testAnalyse")}}:</span>
+                                </div>
+                                <div style="margin-left: 10px;" v-html="question.explain != '' ?  question.explain : $t('studentWeb.exam.report.noAnalyse') "></div>
+                            </div>
+                            <div>
+                                <div v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score" class="TitleRec2"><span style="margin-left:5px">{{$t("studentWeb.exam.report.repairSource")}}:</span></div>
+                                <div style="margin-left: 10px; display: flex" v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score">
+                                    <span v-if="question.repair.length == 0">{{$t("studentWeb.exam.report.noSource")}}</span>
+                                    <div v-if="question.repair && question.repair.length > 0" class="repair-box">
+                                        <Collapse style="width:85%" accordion @on-change="getSource(question.repair,question)">
+                                            <!-- 网络资源 -->
+                                            <Panel name="1">
+                                                {{$t("studentWeb.exam.report.linkSource")}}
+                                                <p slot="content">
+                                                    <List border size="small">
+                                                        <ListItem v-for="(item,normalIndex) in repairSource.normal" :key="normalIndex">
+                                                            <span style="margin-right: 10px;" v-show="item.blobUrl">
+                                                                <Icon color="#0066FF" custom="iconfont icon-share_link" size="20" />
+                                                            </span><a :href="item.blobUrl" target="_blank">{{item.name}}</a>
+                                                        </ListItem>
+                                                        <ListItem v-show="repairSource.normal.length == 0">
+                                                            <span>{{$t("studentWeb.exam.report.noSource")}}</span>
+                                                        </ListItem>
+                                                    </List>
+                                                </p>
+                                            </Panel>
+                                            <!-- 文件资源 -->
+                                            <Panel name="2">
+                                                {{$t("studentWeb.exam.report.fileSource")}}
+                                                <p slot="content">
+                                                    <List border size="small">
+                                                        <ListItem v-for="(item,fileIndex) in repairSource.file" :key="fileIndex">
+                                                            <div style="width:100%">
+                                                                <span style="margin-right:10px;">
+                                                                    <Icon v-if="item.fileType == 'zip'" custom="iconfont icon-zip" size="20" />
+                                                                    <!-- <Icon v-else-if="item.fileType == 'zip'" color="#8199AF" custom="iconfont icon-zip" size="20" /> -->
+                                                                    <Icon v-else-if="item.fileType == 'pdf'" color="#FF6464" custom="iconfont icon-pdf" size="20" />
+                                                                    <Icon v-else-if="item.fileType == 'ppt'|| item.fileType == 'pptx'" color="#FF8976" custom="iconfont icon-ppt" size="20" />
+                                                                    <Icon v-else-if="item.fileType == 'mp3'" color="#FF5562" custom="iconfont icon-mp3" size="20" />
+                                                                    <Icon v-else-if="item.fileType == 'mp4'" color="#8E4C9E" custom="iconfont icon-video1" size="20" />
+                                                                    <!-- <Icon v-else-if="item.fileType == 'zip'" custom="iconfont icon-video1" size="20" /> -->
+                                                                    <Icon v-else-if="item.fileType == 'doc'||item.fileType =='docx'" color="#6CCBFF" custom="iconfont icon-word" size="20" />
+                                                                    <Icon v-else-if="item.fileType == 'csv'||item.fileType =='xlsx'||item.fileType =='xls'" color="#25C273" custom="iconfont icon-xlsx" size="20" />
+                                                                    <Icon v-else-if="item.fileType == 'jpg'||item.fileType =='png'||item.fileType =='jpeg'" color="#30D1CA" custom="iconfont icon-jpg" size="20" />
+                                                                    <Icon v-else custom="iconfont icon-V" size="20" />
+                                                                </span><a @click="getItemData(item)">{{item.name}}</a>
+                                                                <span style="display:block;float:right;cursor:pointer">
+                                                                    <Icon type="md-download" size="18" @click="downloadFile(item)" />
+                                                                </span>
+                                                            </div>
+                                                        </ListItem>
+                                                        <ListItem v-show="repairSource.file.length == 0">
+                                                            <span>{{$t("studentWeb.exam.report.noSource")}}</span>
+                                                        </ListItem>
+                                                    </List>
+                                                </p>
+                                            </Panel>
+                                        </Collapse>
+                                    </div>
                                 </div>
                             </div>
                         </div>
@@ -354,6 +374,7 @@
                 testState: 0, //1:未作答  2:未评分  3:已评分
                 paperData: [], //所有题目信息
                 ansData: [], //题目的作答答案
+                markData: [], //答案的批注
                 repairSource: {
                     normal: [], //网络资源
                     file: [] //文件资源
@@ -509,18 +530,18 @@
                 let paper = []
                 this.paperData.length = 0
                 this.ansData.length = 0
-                let data = this.getPaperInfo.item
-                let exam = [...data]
-                if (this.getPaperInfo.item.length) {
+                this.markData.length = 0
+                let exam = [...this.getPaperInfo.item]
+                if (exam.length) {
                     for (let i = 0; i < exam.length; i++) {
-                        if (exam[i].repair == undefined) {
+                        if (!exam[i].repair) {
                             exam[i].repair = []
                         }
                         if (exam[i].type == 'compose') {
                             let k = 1
                             if (exam[i].children.length > 0) {
                                 for (let items of exam[i].children) {
-                                    if (items.repair == undefined) {
+                                    if (!items.repair) {
                                         items.repair = []
                                     }
                                     items.parentInfo = exam[i]
@@ -538,7 +559,22 @@
                 }
                 this.paperData = [...paper]
                 if (this.paperData.length) {
-                    this.ansData= await this.getItem(this.examInfo.stuAns[0])
+                    this.ansData = await this.getItem(this.examInfo.stuAns[0])
+                    this.markData = new Array(this.examInfo.mark.length)
+                    this.examInfo.mark.map(async (item,index) => {
+                        if(item.length) {
+                            for(let i in item) {
+                                item[i].imgUrl = await this.getMark(item[i].mark)
+                                if(!this.markData[index]){
+                                    this.$set(this.markData,index,[])
+                                }
+                                this.markData[index].push({
+                                    imgUrl:item[i].imgUrl
+                                })
+                            }
+                        }
+                        return item
+                    })
                 }
             },
             // 获取学生作答数据
@@ -559,6 +595,22 @@
                     return []
                 }
             },
+            // 获取批注图片地址
+            async getMark(item) {
+                if (item) {
+                    let urlImg = ""
+                    let code = this.getItemTitle.scope === 'school' ? this.getItemTitle.school : this.getItemTitle.creatorId
+                    let profile = localStorage.student_profile || localStorage.user_profile
+                    let blobUrl = JSON.parse(decodeURIComponent(profile, "utf-8")).blob_uri
+                    let host = blobUrl.substring(0, blobUrl.lastIndexOf('/'))
+                    // 获取授权码('?授权码')
+                    let sas = await this.$tools.getBlobSas(code)
+                    urlImg = `${host}/${code}/${item}${sas}`
+                    return urlImg
+                } else {
+                    return ""
+                }
+            },
             // 处理学生作答数据blob地址
             formUrl(data) { 
                 let a = ""
@@ -835,17 +887,23 @@
     }
 
     .TitleRec1 {
+        margin-top: 10px;
+        margin-bottom: 5px;
         position: relative;
-        height:15px;
+        height: 15px;
+        line-height: 15px;
         border-left: 10px solid #1472c7;
-        float: left;
+        /* float: left; */
     }
 
     .TitleRec2 {
+        margin-top: 10px;
+        margin-bottom: 5px;
         position: relative;
         height: 15px;
+        line-height: 15px;
         border-left: 10px solid gray;
-        float: left;
+        /* float: left; */
     }
 
     .TitleRec3 {

+ 2 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue

@@ -144,6 +144,8 @@
                                         this.paperData[i].stuScore = []
                                     } else {
                                         this.paperData[i].stuAns = resData.stuAns[i]
+                                        // 批注
+                                        this.paperData[i].mark = resData.mark[i]
                                         /* if (resData.mark[i].length != 0) {
                                             this.paperData[i].stuAns = [resData.mark[i]]
                                         } else {

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js

@@ -351,6 +351,7 @@ export default {
             wrong: 'Answer Incorrectly',
             noScore: 'Not Gradedd',
             ansRes: 'Answering Result',
+            mark: '批注',
             testAns: 'Reference Answer',
             testAnalyse: 'Explanation',
             repairSource: 'Remediation Resource',
@@ -361,6 +362,7 @@ export default {
             fileView: 'Preview File',
             noReview: 'This file does not support preview, please download and view! ',
             pdfErr: 'Failed to load PDF',
+            noMark: '暂无批注',
             noAnalyse: 'No explanation yet',
             wrongPractice: 'Incorrectly Answered Questions Practice',
         },

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js

@@ -352,6 +352,7 @@ export default {
             wrong: '答错',
             noScore: '未评分',
             ansRes: '作答结果',
+            mark: '批注',
             testAns: '参考答案',
             testAnalyse: '解析',
             repairSource: '补救资源',
@@ -362,6 +363,7 @@ export default {
             fileView: '文件预览',
             noReview: '该文件暂不支持预览,请下载查看!',
             pdfErr: 'pdf 加载失败',
+            noMark: '暂无批注',
             noAnalyse: '暂无解析',
             wrongPractice: '错题练习',
         },

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js

@@ -351,6 +351,7 @@ export default {
             wrong: '答錯',
             noScore: '未評分',
             ansRes: '作答結果',
+            mark: '批註',
             testAns: '參考答案',
             testAnalyse: '解析',
             repairSource: '補救資源',
@@ -361,6 +362,7 @@ export default {
             fileView: '檔案預覽',
             noReview: '該檔案暫不支持預覽,請下載查看! ',
             pdfErr: 'pdf 載入失敗',
+            noMark: '暂无批註',
             noAnalyse: '暫無解析',
             wrongPractice: '錯題練習',
         },

+ 1 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue

@@ -444,7 +444,7 @@ export default {
                     })
                 },
                 err => {
-                    console.log('保存失败', err)
+                    this.$Message.error(this.$t('learnActivity.mark.saveErr'))
                 }
             )
         },

+ 0 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue

@@ -182,7 +182,6 @@ export default {
             let end = this.pageSize * page
             this.currentPage = page
             this.tableData = this.studentScore.slice(start, end)
-            console.log('表格数据', this.tableData)
         },
         toggleScoreStatus() {
             this.$refs['paperScore'].isComplete = false

+ 9 - 4
TEAMModelOS/ClientApp/src/view/task/index.vue

@@ -217,12 +217,11 @@ export default {
          * 按人阅卷
          */
         async toByStuView(stuId) {
-            console.log('学生id', stuId)
             sessionStorage.setItem('markFrom', this.$route.name)
             let sas = this.$store.state.user.schoolProfile.blob_sas //目前只有校本评测安排阅卷任务
             let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri //目前只有校本评测安排阅卷任务
 
-            let answer, score, sId, quScore, stuData
+            let answer, score, sId, quScore, stuData, markArr
             //批阅已经分配的学生
             if (stuId) {
                 let stuInfo = this.markData.objs.find(item => {
@@ -236,6 +235,9 @@ export default {
                 score = stuInfo.item.map(scoreItem => {
                     return scoreItem.sc
                 })
+                markArr = stuInfo.item.map(item => {
+                    return item.blob
+                })
                 quScore = stuInfo.item.map(scoreItem => {
                     return scoreItem.ssc
                 })
@@ -244,7 +246,6 @@ export default {
             // 获取新学生
             else {
                 let resData = await this.getNextStu()
-                console.log('数据', resData)
                 if (resData.blob && !resData.msg) {
                     let index = resData.tIds.indexOf(this.$store.state.userInfo.TEAMModelId)
                     // let ansBlob = index > -1 ? resData.marks[index] || resData.blob : resData.blob //批注数据结构调整,不是完整的作答数据了
@@ -254,6 +255,9 @@ export default {
                     score = resData.item.map(item => {
                         return item.sc
                     })
+                    markArr = resData.item.map(item => {
+                        return item.blob
+                    })
                     quScore = resData.item.map(item => {
                         return item.ssc
                     })
@@ -275,7 +279,8 @@ export default {
                     stuData,
                     answer,
                     score,
-                    quScore
+                    quScore,
+                    markArr
                 }
             })
         },

+ 1 - 0
TEAMModelOS/ClientApp/src/view/task/mark/ByQu.vue

@@ -252,6 +252,7 @@ export default {
                     }),
                     count: this.taskInfo.count,
                     code: this.taskInfo.ecode.replace('Exam-', ''),
+                    qu: this.taskInfo.qu && this.taskInfo.qu.length ? this.taskInfo.qu : undefined, //按人阅卷不用传这个参数
                     mark
                 }
                 this.$api.mark.saveScore(requstData).then(

+ 59 - 62
TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue

@@ -142,6 +142,7 @@
 </template>
 
 <script>
+import BlobTool from '@/utils/blobTool.js'
 import html2canvas from 'html2canvas'
 import MarkCanvas from './MarkCanvas'
 import StuProg from './StuProg.vue'
@@ -181,7 +182,8 @@ export default {
             stusData: [],
             stuId: '',
             markImg: undefined,
-            quScore: [] //题目配分数据
+            quScore: [], //题目配分数据
+            markArr: []
         }
     },
     methods: {
@@ -256,7 +258,10 @@ export default {
                 let bodyWidth = answerIframe.contentWindow.document.body.clientWidth
                 answerIframe.style.width = (bodyWidth + 20) + 'px'
                 answerIframe.contentWindow.document.body.style.backgroundColor = '#f5f5f5'
-                html2canvas(answerIframe.contentWindow.document.body, {}).then((canvas) => {
+                html2canvas(answerIframe.contentWindow.document.body, {
+                    // proxy: 'https://teammodelstorage.blob.core.chinacloudapi.cn',
+                    useCORS: true
+                }).then((canvas) => {
                     canvas.id = "canvas" + this.quIndex
                     this.ansImg = canvas.toDataURL()
                     // 将转出来的答案绘制到canvas上
@@ -276,13 +281,35 @@ export default {
         submit() {
             if (this.score > -1 && this.score != null) {
                 //保存批注
-                let mark
+                let path, fileName
                 if (this.markImg) {
-                    let img = document.createElement('img')
-                    img.src = this.markImg
-                    this.stuAnswer[this.quIndex] = img.outerHTML
-                    mark = this.stuAnswer
+                    // 第一次批注
+                    if (!this.markArr[this.quIndex]) {
+                        fileName = this.$jsFn.uuid() + '.png'
+                    }
+                    //已有批注
+                    else {
+                        let blob = this.markArr[this.quIndex]
+                        fileName = blob.substring(blob.lastIndexOf('/') + 1,blob.length)
+                    }
+                    let markPng = this.$jsFn.dataURLtoFile(this.markImg, fileName)
+                    //保存批注图片
+                    let sas = this.$store.state.user.schoolProfile.blob_sas
+                    let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
+                    let host = blobUrl.substring(0, blobUrl.lastIndexOf('/'))
+                    let cont = blobUrl.substring(blobUrl.lastIndexOf('/') + 1)
+                    let blobTool = new BlobTool(host, cont, '?' + sas, 'school')
+                    path = `exam/${this.taskInfo.cid}/${this.taskInfo.subject}/${this.stuId}`
                     this.markImg = undefined
+                    blobTool.upload(markPng, path).then(
+                        res => {
+                            this.$Message.success('批注保存成功')
+                        },
+                        err => {
+                            console.log('保存失败', err)
+                        }
+                    )
+                    this.markArr.splice(this.quIndex, 1, `${path}/${fileName}`)
                 }
                 let requstData = {
                     id: this.taskInfo.cid,
@@ -292,8 +319,8 @@ export default {
                     score: this.stuScore,
                     count: this.taskInfo.count,
                     code: this.taskInfo.ecode.replace('Exam-', ''),
-                    // qu: this.taskInfo.qu, //按人阅卷不用传这个参数
-                    mark
+                    qu: this.taskInfo.qu && this.taskInfo.qu.length ? this.taskInfo.qu : undefined, //按人阅卷不用传这个参数
+                    mark: path && fileName ? this.markArr : undefined
                 }
                 this.$api.mark.saveScore(requstData).then(
                     res => {
@@ -313,54 +340,6 @@ export default {
         getMarkData(data) {
             this.markImg = data.base64
         },
-        saveMark() {
-            let fileName = this.$jsFn.uuid() + '.png'
-            let markPng = this.$jsFn.dataURLtoFile(this.markImg, fileName)
-            //保存批注图片
-            let sas = this.$store.state.user.schoolProfile.blob_sas
-            let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
-            let host = blobUrl.substring(0, blobUrl.lastIndexOf('/'))
-            let cont = blobUrl.substring(blobUrl.lastIndexOf('/') + 1)
-            let blobTool = new BlobTool(host, cont, '?' + sas, this.examScope)
-            let path = `exam/${this.taskInfo.id}/${this.taskInfo.subject}/${this.stuId}`
-            blobTool.upload(markPng, path).then(
-                res => {
-                    this.studentAnswer.mark[this.curAnIndex].push({
-                        mark: path + '/' + fileName
-                    })
-                    this.markStatus = false
-                    //保存批注数据
-                    this.$api.learnActivity.upsertAnswer({
-                        "id": this.taskInfo.id,
-                        "studentId": this.stuId,
-                        "subjectId": this.taskInfo.subject,
-                        // "classId": this.studentAnswer.classId, //阅卷这边没有班级信息
-                        "code": this.$store.state.userInfo.schoolCode,
-                        "tmdId": this.$store.state.userInfo.TEAMModelId,
-                        "index": this.quIndex,//题号
-                        "mark": {
-                            sc: 0,//分数
-                            tmdId: this.$store.state.userInfo.TEAMModelId,
-                            mark: path + '/' + fileName,//批注BLOB地址
-                            identity: this.taskInfo.type == 1 ? 'reviewer' : '',//老师身份,这是是固定管理员身份
-                            index: this.quIndex //题号
-                        }
-                    }).then(
-                        res => {
-                            this.$Message.success(this.$t('learnActivity.score.markOk'))
-                        },
-                        err => {
-                            this.$Message.error(this.$t('learnActivity.score.markErr'))
-                        }
-                    ).finally(() => {
-                        this.curAnIndex = -1
-                    })
-                },
-                err => {
-                    console.log('保存失败', err)
-                }
-            )
-        },
         nextQuestion() {
             //首先判断是否都已评分
             let s = this._.cloneDeep(this.stuScore)
@@ -372,8 +351,8 @@ export default {
                 })
             }
             //筛选客观题(自动评分)
-            else{
-                s = s.filter((item,index)=>{
+            else {
+                s = s.filter((item, index) => {
                     return !this.quNoList[index].disabled
                 })
             }
@@ -448,6 +427,9 @@ export default {
                 this.stuScore = stuInfo.item.map(item => {
                     return item.sc
                 })
+                this.markArr = stuInfo.item.map(item => {
+                    return item.blob
+                })
                 this.stuId = stuId
             }
             // 随机获取一名学生
@@ -476,7 +458,7 @@ export default {
         getNextStu(stuId) {
             let requestData = {
                 code: this.taskInfo.ecode.replace('Exam-', ''),
-                id: this.taskInfo.id,
+                id: this.taskInfo.cid,
                 subjectId: this.taskInfo.subject,
                 count: this.taskInfo.count,
                 tmdId: this.$store.state.userInfo.TEAMModelId,
@@ -507,6 +489,18 @@ export default {
                 }
             )
         },
+        //获取批注数据
+        getMarkHtml(blob) {
+            let sas = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_sas
+            let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
+            let url = blobUrl + '/' + blob + '?' + sas
+            console.log('hehehe', url)
+            let h = `<img src=\"${url}\"/>`
+            return [h]
+
+            //直接赋值可以渲染,当时stage.toDataUrl会报错
+            // this.ansImg = url
+        },
     },
     mounted() {
         this.ansToImg()
@@ -521,6 +515,7 @@ export default {
         this.taskInfo = routeData.task
         this.stuId = routeData.stuId
         this.stuData = routeData.stuData
+        this.markArr = routeData.markArr
         console.log('路由数据', routeData)
         if (this.paperData && this.stuAnswer && this.stuScore && this.quScore && this.taskInfo && this.stuId) {
             //初始化题目index
@@ -550,12 +545,14 @@ export default {
             if (this.stuAnswer.length) {
                 let index = this.quIndex
                 this.score = this.stuScore[index] == -1 ? null : this.stuScore[index]
-                return this.stuAnswer[index]
+                let answerOrMark = this.markArr[index] ? this.getMarkHtml(this.markArr[index]) : this.stuAnswer[index]
+                console.log('批注或答案', answerOrMark)
+                return answerOrMark
             } else {
                 return this.stuId + this.$t('learnActivity.mark.noAnswer')
             }
-
         },
+
         //试卷题号列表
         quNoList() {
             if (this.paperData.item.length) {

+ 10 - 4
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -1608,14 +1608,15 @@ namespace TEAMModelOS.Controllers
                     foreach (Item item in ss.items)
                     {
                         List<double> scc = item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.sc).ToList();
+                        List<string> blob = item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.mark).ToList();
                         //item.scores.Where(x => x.tmdId.Equals(tId.GetString())).SelectMany(p => p.sc, (p, d) => new { });
                         if (scc.Count > 0)
                         {
-                            sc.Add(new { sc = scc.FirstOrDefault(), item.ssc });
+                            sc.Add(new { sc = scc.FirstOrDefault(),blob = blob.FirstOrDefault(), item.ssc });
                         }
                         else
                         {
-                            sc.Add(new { sc = ss.scores[indexScore], item.ssc });
+                            sc.Add(new { sc = ss.scores[indexScore], blob="",item.ssc });
                         }
                         indexScore++;
                         //sc.Add(item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.sc).FirstOrDefault());
@@ -2047,7 +2048,7 @@ namespace TEAMModelOS.Controllers
                 if (!requert.TryGetProperty("count", out JsonElement count)) return BadRequest();
                 if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
                 //if (!requert.TryGetProperty("mark", out JsonElement mark)) return BadRequest();
-                //requert.TryGetProperty("mark", out JsonElement mark);
+                requert.TryGetProperty("mark", out JsonElement mark);
                 var client = _azureCosmos.GetCosmosClient();
                 //var redisClient = _azureRedis.GetRedisClient(8);
                 List<ExamClassResult> classResults = new();
@@ -2084,6 +2085,7 @@ namespace TEAMModelOS.Controllers
                 value.RootElement.TryGetProperty("info", out JsonElement element);
                 List<tmdInfo> tmds = element.ToObject<List<tmdInfo>>();*/
                 List<double> ssc = score.ToObject<List<double>>();
+                List<string> marks = mark.ToObject<List<string>>();
                 StringBuilder builder = new();
                 /*if (requert.TryGetProperty("mark", out JsonElement mark))
                 {
@@ -2151,6 +2153,7 @@ namespace TEAMModelOS.Controllers
                                     foreach (Info info in items)
                                     {
                                         info.sc = ssc[number];
+                                        info.mark = marks.Count > 0 ? marks[number]:"";
                                     }
                                 }
                                 else
@@ -2158,6 +2161,7 @@ namespace TEAMModelOS.Controllers
                                     Info info = new();
                                     info.sc = ssc[number];
                                     info.tmdId = tId.GetString();
+                                    info.mark = marks.Count > 0 ? marks[number] : "";
                                     info.index = number;
                                     scoring.items[number].scores.Add(info);
                                 }
@@ -2173,6 +2177,7 @@ namespace TEAMModelOS.Controllers
                                     foreach (Info info in items)
                                     {
                                         info.sc = ssc[itemIndex];
+                                        info.mark = marks.Count > 0 ? marks[itemIndex] : "";
                                     }
                                 }
                                 else
@@ -2180,6 +2185,7 @@ namespace TEAMModelOS.Controllers
                                     Info info = new();
                                     info.sc = ssc[itemIndex];
                                     info.tmdId = tId.GetString();
+                                    info.mark = marks.Count > 0 ? marks[itemIndex] : "";
                                     info.index = itemIndex;
                                     item.scores.Add(info);
                                 }
@@ -2217,7 +2223,7 @@ namespace TEAMModelOS.Controllers
                                 var ace = item.scores.Where(x => string.IsNullOrEmpty(x.tmdId)).ToList();
                                 if (ace.Count > 0)
                                 {
-                                    return Ok(new { code = 200 });
+                                    return Ok(new { code = 404 });
                                 }
                                 //判定是否仲裁卷
                                 if (!item.flag)

+ 16 - 7
TEAMModelOS/Controllers/Common/SurveyController.cs

@@ -89,6 +89,7 @@ namespace TEAMModelOS.Controllers
                 if (string.IsNullOrEmpty(request.id))
                 {
                     request.id = Guid.NewGuid().ToString();
+                   
                     if (request.startTime > now)
                     {
                         request.progress = "pending";
@@ -98,31 +99,41 @@ namespace TEAMModelOS.Controllers
                         request.progress = "going";
                     }
                     var messageBlob = new ServiceBusMessage();
+                    string blobcntr = null;
                     if (request.scope.Equals("school"))
                     {
+                        blobcntr = request.school;
                         request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"survey/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"survey/{request.id}", name = $"{request.school}" }.ToJsonString());
                     }
                     else
                     {
+                        blobcntr = request.creatorId;
                         request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"survey/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"survey/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
                     }
                     messageBlob.ApplicationProperties.Add("name", "BlobRoot");
                     var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
                     await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
+                    request.recordUrl = $"/survey/{request.id}/record.json";
+                    var cods = new { records = new List<string>(), userids = new List<string>(), question = new List<QuestionRecord>() };
+                    await _azureStorage.UploadFileByContainer(blobcntr, cods.ToJsonString(), "survey", $"{request.id}/record.json");
                     request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
-                else {
+                else
+                {
+                    string blobcntr = null;
                     var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
                     var messageBlob = new ServiceBusMessage();
                     if (request.scope.Equals("school"))
                     {
+                        blobcntr = request.school;
                         request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"survey/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"survey/{request.id}", name = $"{request.school}" }.ToJsonString());
                     }
                     else
                     {
+                        blobcntr = request.creatorId;
                         request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"survey/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"survey/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
                     }
@@ -157,6 +168,9 @@ namespace TEAMModelOS.Controllers
                         {
                             request.progress = "going";
                         }
+                        request.recordUrl = $"/survey/{request.id}/record.json";
+                        var cods = new { records = new List<string>(), userids = new List<string>(), question = new List<QuestionRecord>() };
+                        await _azureStorage.UploadFileByContainer(blobcntr, cods.ToJsonString(), "survey", $"{request.id}/record.json");
                         request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
                 }
@@ -535,10 +549,5 @@ namespace TEAMModelOS.Controllers
 
 
     }
-    public class QuestionRecord
-    {
-        public int index { get; set; }
-        public Dictionary<string, HashSet<string>> opt { get; set; } = new Dictionary<string, HashSet<string>>();
-        public Dictionary<string, string> other { get; set; } = new Dictionary<string, string>();
-    }
+  
 }

+ 15 - 24
TEAMModelOS/Controllers/Common/VoteController.cs

@@ -93,35 +93,44 @@ namespace TEAMModelOS.Controllers.Learn
                         request.progress = "pending";
                     }
                     else { 
-                        request.progress = "going"; 
+                        request.progress = "going";
                     }
+                    string blobcntr = null;
                     var messageBlob = new ServiceBusMessage();
                     if (request.scope.Equals("school"))
                     {
+                        blobcntr = request.school;
                         request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"vote/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"vote/{request.id}", name = $"{request.school}" }.ToJsonString());                       
                     }
                     else
                     {
+                        blobcntr = request.creatorId;
                         request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"vote/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"vote/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
                     }
                     messageBlob.ApplicationProperties.Add("name", "BlobRoot");
                     var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
                     await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
+                    string url = $"/vote/{request.id}/record.json";
+                    request.recordUrl = url;
+                    await _azureStorage.UploadFileByContainer(blobcntr, new { options = new List<string>(), records = new List<VoteRecord>() }.ToJsonString(), "vote", $"{request.id}/record.json");
                     request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
                 else
                 {
                     var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
                     var messageBlob = new ServiceBusMessage();
+                    string blobcntr = null;
                     if (request.scope.Equals("school"))
                     {
+                        blobcntr = request.school;
                         request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"vote/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"vote/{request.id}", name = $"{request.school}" }.ToJsonString());
                     }
                     else
                     {
+                        blobcntr = request.creatorId;
                         request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"vote/{request.id}");
                         messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"vote/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
                     }
@@ -157,6 +166,9 @@ namespace TEAMModelOS.Controllers.Learn
                         {
                             request.progress = "going";
                         }
+                        string url = $"/vote/{request.id}/record.json";
+                        request.recordUrl = url;
+                        await _azureStorage.UploadFileByContainer(blobcntr, new { options = new List<string>(), records = new List<VoteRecord>() }.ToJsonString(), "vote", $"{request.id}/record.json");
                         request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
                 }
@@ -267,27 +279,6 @@ namespace TEAMModelOS.Controllers.Learn
                         votes.Add(item);
                     }
                 }
-                //List<object> votes = new List<object>();
-                //var client = _azureCosmos.GetCosmosClient();
-
-                //var query =$"select   c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where c.createTime >= {stimestamp} and c.createTime <= {etimestamp}  {progresssql } ";
-                //await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query,
-                //    requestOptions: new QueryRequestOptions() {MaxItemCount = topcout, PartitionKey = new PartitionKey($"Vote-{code}") }))
-                //{
-                //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                //    {
-                //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                //        {
-                //            votes.Add(obj.ToObject<JsonElement>());
-                //        }
-                //        //如果需要分页则跳出
-                //        if (iscontinuation) {
-                //            continuationToken = item.GetContinuationToken();
-                //            break;
-                //        }
-                //    }
-                //}
                 return Ok(new { votes, continuationToken });
             }
             catch (Exception ex)
@@ -501,7 +492,7 @@ namespace TEAMModelOS.Controllers.Learn
         public async Task<IActionResult> Decide(JsonElement request)
         {
             var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
-            int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, userid);
+            int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis ,_azureStorage, userid);
             return Ok(new { msgid });
         }
         /// <summary>
@@ -528,7 +519,7 @@ namespace TEAMModelOS.Controllers.Learn
             //活动id
             if (request.TryGetProperty("userid", out JsonElement userid))
             {
-                int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, $"{userid}");
+                int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, _azureStorage, $"{userid}");
                 return Ok(new { msgid });
             }
             else { return Ok(new { msgid = 0 }); }

+ 78 - 31
TEAMModelOS/Controllers/School/CourseController.cs

@@ -61,7 +61,7 @@ namespace TEAMModelOS.Controllers
         {
             try
             {
-                (string id ,_,_,string school )   = HttpContext.GetAuthTokenInfo();
+                (string id, _, _, string school) = HttpContext.GetAuthTokenInfo();
                 Course course = new Course();
                 if (!requert.TryGetProperty("course", out JsonElement _course)) return BadRequest();
                 if (!requert.TryGetProperty("option", out JsonElement option)) return BadRequest();
@@ -74,7 +74,8 @@ namespace TEAMModelOS.Controllers
                     course.school = school;
                     course.creatorId = id;
                 }
-                else {
+                else
+                {
                     course.creatorId = id;
                 }
                 string code = course.code;
@@ -229,14 +230,54 @@ namespace TEAMModelOS.Controllers
             }
 
         }
+
+
+        [ProducesDefaultResponseType]
+        [HttpPost("check")]
+        public async Task<IActionResult> check(JsonElement json)
+        {
+            try
+            {
+                if (!json.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
+                if (!json.TryGetProperty("timeId", out JsonElement timeId)) return BadRequest();
+                if (!json.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                var client = _azureCosmos.GetCosmosClient();
+                var query = $"SELECT A0 FROM c join A0 in c.schedule join A1 in A0.time where A0.teacherId = '{tId}' and A1.id = '{timeId}'";
+                List<object> courses = new();
+                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{code}") }))
+                {
+                    using var jsonCheck = await JsonDocument.ParseAsync(item.ContentStream);
+                    if (jsonCheck.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in jsonCheck.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            courses.Add(obj.ToObject<object>());
+                        }
+                    }
+                }
+                if (courses.Count > 0)
+                {
+                    return Ok(new { status = -1 });
+                }
+                else {
+                    return Ok(new { status = 0 });
+                }
+                
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},course/get-list-by-no()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+        }
         /*
         {
             "stuListNo": "1124582",
             "studentId":  "19990005",//tmdId/studentId 只能是其中一个有值
             "tmdId":"1538614518",
-            
+
         }
-            
+
          */
         /// <summary>
         /// 扫码加入名单
@@ -251,7 +292,7 @@ namespace TEAMModelOS.Controllers
             {
 
                 if (!json.TryGetProperty("stuListNo", out JsonElement _stuListNo)) return BadRequest();
-               
+
                 json.TryGetProperty("school", out JsonElement _school);
                 string school = $"{_school}";
                 var client = _azureCosmos.GetCosmosClient();
@@ -259,7 +300,7 @@ namespace TEAMModelOS.Controllers
                 StuList stuList = null;
                 json.TryGetProperty("studentId", out JsonElement _studentId);
                 json.TryGetProperty("tmdId", out JsonElement _tmdId);
-                if (!string.IsNullOrEmpty(school)&&!string.IsNullOrEmpty($"{_studentId}") )
+                if (!string.IsNullOrEmpty(school) && !string.IsNullOrEmpty($"{_studentId}"))
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryNo,
                           requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"StuList-{school}") }))
@@ -320,8 +361,10 @@ namespace TEAMModelOS.Controllers
         [ProducesDefaultResponseType]
         [AuthToken(Roles = "admin,teacher,student")]
         [HttpPost("get-list-by-no")]
-        public async Task<IActionResult> GetListByNo(JsonElement json) {
-            try {
+        public async Task<IActionResult> GetListByNo(JsonElement json)
+        {
+            try
+            {
 
                 if (!json.TryGetProperty("stuListNo", out JsonElement _stuListNo)) return BadRequest();
                 var (userid, _, _, school) = HttpContext.GetAuthTokenInfo();
@@ -346,7 +389,7 @@ namespace TEAMModelOS.Controllers
                                 if (stuList != null)
                                 {
                                     (int status, StuList stuLis) = JoinList(stuList, $"{_studentId}", $"{_tmdId}", school);
-                                    stuLis=await upsertList(stuList, "school");
+                                    stuLis = await upsertList(stuList, "school");
                                     return Ok(new { status, stuLis });
                                 }
                             }
@@ -373,20 +416,22 @@ namespace TEAMModelOS.Controllers
                         }
                     }
                 }
-                return Ok(new { status= - 1 });
-            } catch (Exception ex) {
+                return Ok(new { status = -1 });
+            }
+            catch (Exception ex)
+            {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},course/get-list-by-no()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
         }
 
-       public  (int status, StuList stuList) JoinList(StuList stuList, string _studentId,string _tmdId,string school)
-       {
+        public (int status, StuList stuList) JoinList(StuList stuList, string _studentId, string _tmdId, string school)
+        {
             int status = -1;
             if (string.IsNullOrEmpty($"{_studentId}") && string.IsNullOrEmpty($"{_tmdId}"))
             {
                 //加入学生或醍摩豆ID为空
-                status=1;
+                status = 1;
             }
             else
             {
@@ -397,7 +442,7 @@ namespace TEAMModelOS.Controllers
                     if (student != null)
                     {
                         //重复加入
-                        status= 2  ;
+                        status = 2;
                     }
                     else
                     {
@@ -421,7 +466,7 @@ namespace TEAMModelOS.Controllers
                     }
                 }
             }
-            return (status,stuList);
+            return (status, stuList);
         }
         //处理通用名单
         [ProducesDefaultResponseType]
@@ -452,7 +497,7 @@ namespace TEAMModelOS.Controllers
             try
             {
                 var client = _azureCosmos.GetCosmosClient();
-                
+
                 //todo 需要校验是否重复
                 if (string.IsNullOrEmpty(stuList.no))
                 {
@@ -510,17 +555,17 @@ namespace TEAMModelOS.Controllers
                     }
                 }
 
-                stuList.code = stuList.pk + "-" + stuList.code.Replace("StuList-","");
+                stuList.code = stuList.pk + "-" + stuList.code.Replace("StuList-", "");
                 if (scope.ToString().Equals("school", StringComparison.OrdinalIgnoreCase))
                 {
                     stuList.scope = "school";
                     StuListChange change = new StuListChange()
                     {
-                        listid= stuList.id,
+                        listid = stuList.id,
                         scope = $"{scope}",
                         originCode = stuList.school,
-                        school=stuList.school,
-                        creatorId=stuList.creatorId
+                        school = stuList.school,
+                        creatorId = stuList.creatorId
                     };
                     var query = $"SELECT distinct value(c)  FROM c where  c.id='{stuList.id}'";
                     List<StuList> odlStus = new List<StuList>();
@@ -557,7 +602,7 @@ namespace TEAMModelOS.Controllers
                         }
                         if (stuList.tmids != null)
                         {
-                            if (odlStus[0].tmids!=null)
+                            if (odlStus[0].tmids != null)
                             {
                                 StuList oldStu = odlStus[0];
                                 foreach (var tmdid in stuList.tmids)
@@ -593,7 +638,7 @@ namespace TEAMModelOS.Controllers
                     stuList.scope = "private";
                     StuListChange change = new StuListChange()
                     {
-                        listid= stuList.id,
+                        listid = stuList.id,
                         scope = $"{scope}",
                         originCode = stuList.creatorId,
                         school = stuList.school,
@@ -609,10 +654,10 @@ namespace TEAMModelOS.Controllers
                     }
                     if (odlStus.Count > 0)
                     {
-                        if (stuList.students!=null)
+                        if (stuList.students != null)
                         {
 
-                            if (odlStus[0].students!=null)
+                            if (odlStus[0].students != null)
                             {
                                 StuList oldStu = odlStus[0];
                                 foreach (var stu in stuList.students)
@@ -633,9 +678,9 @@ namespace TEAMModelOS.Controllers
                                 }
                             }
                         }
-                        if (stuList.tmids!=null)
+                        if (stuList.tmids != null)
                         {
-                            if (odlStus[0].tmids!=null)
+                            if (odlStus[0].tmids != null)
                             {
                                 StuList oldStu = odlStus[0];
                                 foreach (var tmdid in stuList.tmids)
@@ -655,11 +700,13 @@ namespace TEAMModelOS.Controllers
                                     }
                                 }
                             }
-                            else { 
-                            
+                            else
+                            {
+
                             }
                         }
-                        if (change.tmdjoin.Count != 0 || change.tmdhleave.Count != 0 || change.stujoin.Count != 0 || change.stuleave.Count != 0) {
+                        if (change.tmdjoin.Count != 0 || change.tmdhleave.Count != 0 || change.stujoin.Count != 0 || change.stuleave.Count != 0)
+                        {
                             var messageChange = new ServiceBusMessage(change.ToJsonString());
                             messageChange.ApplicationProperties.Add("name", "StuList");
                             var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
@@ -1444,7 +1491,7 @@ namespace TEAMModelOS.Controllers
                                     newCla.id = cla.id;
                                     newCla.code = cla.pk + "-" + code;
                                     newCla.name = classSimple.name;
-                                   // newCla.scope = classSimple.scope;
+                                    // newCla.scope = classSimple.scope;
                                     /*foreach (StudentSimple student in cla.students)
                                     {
                                         StudentSimple studentSimple = new StudentSimple();

+ 2 - 2
TEAMModelOS/Controllers/Teacher/CommentController.cs

@@ -151,7 +151,7 @@ namespace TEAMModelOS.Controllers
             if (!request.TryGetProperty("studentId", out JsonElement studentId)) return BadRequest();
             if (!request.TryGetProperty("tmdId", out JsonElement tId)) return BadRequest();
             if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
-            if (!request.TryGetProperty("classId", out JsonElement classId)) return BadRequest();
+            //if (!request.TryGetProperty("classId", out JsonElement classId)) return BadRequest();
             if (!request.TryGetProperty("index", out JsonElement itemIndex)) return BadRequest();
             //根据不同评测的类型返回对应的编码
             if (!request.TryGetProperty("code", out JsonElement school)) return BadRequest();
@@ -160,7 +160,7 @@ namespace TEAMModelOS.Controllers
                 var client = _azureCosmos.GetCosmosClient();
                 List<ExamClassResult> examClassResults = new List<ExamClassResult>();
                 await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(
-                    queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}' and c.info.id = '{classId}'",
+                    queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}' and array_contains(c.studentIds,'{studentId}')",
                     requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);

+ 124 - 10
TEAMModelOS/Services/Common/ActivityStudentService.cs

@@ -13,6 +13,7 @@ using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
 using StackExchange.Redis;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.Controllers;
 
 namespace TEAMModelOS.Services.Common
 {
@@ -20,8 +21,8 @@ namespace TEAMModelOS.Services.Common
     {
 
        
-        public static async Task<int> Decide(JsonElement request,AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis,string userid ) {
-
+        public static async Task<int> Decide(JsonElement request,AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis , AzureStorageFactory _azureStorage, string userid ) {
+            Vote vote = null;
             DateTimeOffset now = DateTimeOffset.UtcNow;
             long curr = now.ToUnixTimeMilliseconds();
             byte msgid = 0;//0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
@@ -52,9 +53,8 @@ namespace TEAMModelOS.Services.Common
             {
                 //1.再次检查投票
                 var client = _azureCosmos.GetCosmosClient();
-                Vote vote = null;
                 ///TODO 检查是否在投票范围内,包括在tmdids 及班级  但是需要处理认证金钥中的班级问题
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Vote>(queryText: $"select c.id,c.code , c.progress,c.times,c.voteNum,c.startTime,c.endTime from c where c.id = '{id}'",
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Vote>(queryText: $"select c.id,c.code ,c.school,c.creatorId,c.scope , c.progress,c.times,c.voteNum,c.startTime,c.endTime from c where c.id = '{id}'",
                     requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
                 {
                     vote = item;
@@ -121,11 +121,50 @@ namespace TEAMModelOS.Services.Common
             {
                 throw new Exception(e.StackTrace);
             }
+            if (msgid == 1 && vote!= null) 
+            {
+                string blobcntr = null;
+                if (vote.scope.Equals("school"))
+                {
+                    blobcntr = vote.school;
+                }
+                else
+                {
+                    blobcntr = vote.creatorId;
+                }
+                //获取投票活动的所有投票记录
+                var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{vote.id}");
+                //获取投票活动的选项及投票数
+                var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{vote.id}");
+                List<dynamic> countcds = new List<dynamic>();
+                if (counts != null && counts.Length > 0)
+                {
+                    foreach (var count in counts)
+                    {
+                        countcds.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                    }
+                }
+                List<Task<string>> tasks = new List<Task<string>>();
+                List<VoteRecord> recordsBlob = new List<VoteRecord>();
+                foreach (var rcd in records)
+                {
+                    var value = rcd.Value.ToString().ToObject<VoteRecord>();
+                    recordsBlob.Add(value);
+                }
+                //分组每个人的 
+                var gp = recordsBlob.GroupBy(x => x.userid).Select(x => new { key = x.Key, list = x.ToList() });
+                var userdata = gp.Where(x => x.key.Equals(userid)).FirstOrDefault();
+                if(userdata!=null)
+                {
+                    tasks.Add(_azureStorage.UploadFileByContainer(blobcntr, userdata.list.ToJsonString(), "vote", $"{vote.id}/urecord/{userdata.key}.json"));
+                }
+                //处理活动方的记录, 
+                string url = $"/vote/{vote.id}/record.json";
+                tasks.Add(_azureStorage.UploadFileByContainer(blobcntr, new { options = countcds, records = recordsBlob }.ToJsonString(), "vote", $"{vote.id}/record.json"));
+            }
             return msgid;
         }
 
-      
-
         public static async Task<byte> VoteIng(Vote vote, RedisValue value, byte msgid, Dictionary<string, int> option, string Field, long curr, AzureRedisFactory _azureRedis,string userid,string times,string endpoint)
         {
             if (!value.IsNullOrEmpty)
@@ -396,9 +435,9 @@ namespace TEAMModelOS.Services.Common
                     //判断投票时间是否在起止时间内
                     if (curr >= survey.startTime && curr <= survey.endTime)
                     {
-                        if (request.TryGetProperty("record", out JsonElement record)) 
+                        if (request.TryGetProperty("record", out JsonElement _record)) 
                         {
-                            var recs = record.ToObject<List<List<string>>>();
+                            var recs = _record.ToObject<List<List<string>>>();
                             if (recs.IsNotEmpty() && recs.Count == survey.answers.Count)
                             {
                                 //处理问卷调查表的每一题选项数
@@ -462,12 +501,88 @@ namespace TEAMModelOS.Services.Common
                                 {
                                     blobcntr = survey.school;
                                 }
-                                else if (survey.scope .Equals("private"))
+                                else  
                                 {
                                     blobcntr = survey.creatorId;
                                 }
                                 await _azureStorage.UploadFileByContainer(blobcntr, new SurveyRecord { ans= recs, userid=userid, time = curr }.ToJsonString(), "survey", $"{survey.id}/urecord/{userid}.json");
+                                ///bgn 20210805 huanghb 实时结算
                                 await azureRedis.GetRedisClient(8).SetAddAsync($"Survey:Submit:{survey.id}", userid);
+
+                                var submits = await azureRedis.GetRedisClient(8).SetMembersAsync($"Survey:Submit:{survey.id}");
+                                List<dynamic> userids = new List<dynamic>();
+                                foreach (var submit in submits)
+                                {
+                                    var value = submit.ToString();
+                                    userids.Add(value);
+                                }
+                                List<QuestionRecord> questionRecords = new List<QuestionRecord>();
+                                //结算每道题的答题情况
+                                var ContainerClient = _azureStorage.GetBlobContainerClient(blobcntr);
+                                List<Task<string>> tasks = new List<Task<string>>();
+                                List<string> items = await ContainerClient.List($"survey/{survey.id}/urecord");
+                                List<SurveyRecord> surveyRecords = new List<SurveyRecord>();
+                                foreach (string item in items)
+                                {
+                                    var Download = await _azureStorage.GetBlobContainerClient(blobcntr).GetBlobClient(item).DownloadAsync();
+                                    var json = await JsonDocument.ParseAsync(Download.Value.Content);
+                                    var Record = json.RootElement.ToObject<SurveyRecord>();
+                                    surveyRecords.Add(Record);
+                                }
+                                for (int index = 0; index < survey.answers.Count; index++)
+                                {
+                                    string url = $"{survey.id}/qrecord/{index}.json";
+                                    QuestionRecord question = new QuestionRecord() { index = index };
+                                    foreach (SurveyRecord record in surveyRecords)
+                                    {
+                                        if (record.ans.Count == survey.answers.Count)
+                                        {
+                                            foreach (var an in record.ans[index])
+                                            {
+                                                //
+                                                if (question.opt.ContainsKey(an))
+                                                {
+                                                    if (question.opt[an] != null)
+                                                    {
+                                                        question.opt[an].Add(record.userid);
+                                                    }
+                                                    else
+                                                    {
+                                                        question.opt[an] = new HashSet<string>() { record.userid };
+                                                    }
+                                                }
+                                                else
+                                                {
+                                                    if (survey.answers[index].Contains(an))
+                                                    {
+                                                        //如果是客观题code
+                                                        question.opt.Add(an, new HashSet<string> { record.userid });
+                                                    }
+                                                    else
+                                                    {
+                                                        //如果不是客观code
+                                                        question.other[record.userid] = an;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                    questionRecords.Add(question);
+                                    tasks.Add(_azureStorage.UploadFileByContainer(blobcntr, question.ToJsonString(), "survey", url));
+                                }
+                                var records = await azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{survey.id}");
+                                List<dynamic> recds = new List<dynamic>();
+                                foreach (var rcd in records)
+                                {
+                                    var value = rcd.Value.ToString().ToObject<JsonElement>();
+                                    recds.Add(new { index = rcd.Name.ToString(), ans = value });
+                                }
+                                await Task.WhenAll(tasks);
+                                var cods = new { records = recds, userids, question = questionRecords };
+                                //问卷整体情况
+                                await _azureStorage.UploadFileByContainer(blobcntr, cods.ToJsonString(), "survey", $"{survey.id}/record.json");
+                                ///end 20210805 huanghb 实时结算
+
                                 msgid = 1;
                             }
                             else {
@@ -475,7 +590,6 @@ namespace TEAMModelOS.Services.Common
                                 msgid = 3;
                             }
                         }
-                        
                     }
                     else
                     {