Ver Fonte

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

liqk há 3 anos atrás
pai
commit
fc416dd3e3
48 ficheiros alterados com 1348 adições e 327 exclusões
  1. 265 12
      TEAMModelOS.SDK/Models/Service/FixDataService.cs
  2. 25 6
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/homework-feedback.less
  3. 1 1
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  4. 5 3
      TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue
  5. 27 23
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/ClassmateCommentPages.vue
  6. 31 4
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Homework.vue
  7. 124 37
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/HomeworkFeedback.vue
  8. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  9. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/answerSheet.js
  10. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/courseManage.js
  11. 20 20
      TEAMModelOS/ClientApp/src/locale/lang/en-US/cusMgt.js
  12. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/evaluation.js
  13. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/en-US/global.js
  14. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/en-US/home.js
  15. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/index.js
  16. 10 10
      TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js
  17. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/login.js
  18. 34 34
      TEAMModelOS/ClientApp/src/locale/lang/en-US/notify.js
  19. 15 15
      TEAMModelOS/ClientApp/src/locale/lang/en-US/schoolBaseInfo.js
  20. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/stuAccount.js
  21. 5 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js
  22. 4 4
      TEAMModelOS/ClientApp/src/locale/lang/en-US/survey.js
  23. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/en-US/system.js
  24. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/teachContent.js
  25. 25 25
      TEAMModelOS/ClientApp/src/locale/lang/en-US/teachermgmt.js
  26. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/en-US/totalAnalysis.js
  27. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/utils.js
  28. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js
  29. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/cusMgt.js
  30. 14 14
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/jyzx.js
  31. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/knowledge.js
  32. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js
  33. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/login.js
  34. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/notify.js
  35. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js
  36. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/selflearn.js
  37. 3 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js
  38. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachContent.js
  39. 4 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachermgmt.js
  40. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/user.js
  41. 1 1
      TEAMModelOS/ClientApp/src/view/teachermgmt/Index.vue
  42. 253 6
      TEAMModelOS/Controllers/Client/HiTAControlller.cs
  43. 64 18
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  44. 31 31
      TEAMModelOS/Controllers/Client/SokrateController.cs
  45. 137 18
      TEAMModelOS/Controllers/Common/ExamController.cs
  46. 1 1
      TEAMModelOS/Controllers/School/StudentController.cs
  47. 54 0
      TEAMModelOS/Controllers/XTest/FixDataController.cs
  48. 157 0
      TEAMModelOS/Services/Common/BlobService.cs

+ 265 - 12
TEAMModelOS.SDK/Models/Service/FixDataService.cs

@@ -11,6 +11,7 @@ using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK;
 using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
 
 namespace TEAMModelOS.SDK.Models.Service
 {
@@ -24,7 +25,8 @@ namespace TEAMModelOS.SDK.Models.Service
         /// <param name="_azureStorage"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        public static async Task<List<Student>> FixStudentInfo(CosmosClient client, DingDing _dingDing, AzureStorageFactory _azureStorage, JsonElement data) {
+        public static async Task<List<Student>> FixStudentInfo(CosmosClient client, DingDing _dingDing, AzureStorageFactory _azureStorage, JsonElement data)
+        {
             var code = data.GetProperty("code").GetString();
             var ids = data.GetProperty("ids").ToObject<List<string>>();
             var dict = data.GetProperty("dict").ToObject<Dictionary<string, object>>();
@@ -33,11 +35,14 @@ namespace TEAMModelOS.SDK.Models.Service
             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student")
                             .GetItemQueryIterator<Student>(
                                 queryText: queryText,
-                                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{code}") })) {
-                foreach (var key in dict.Keys) {
-                    switch (key) {
+                                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{code}") }))
+            {
+                foreach (var key in dict.Keys)
+                {
+                    switch (key)
+                    {
                         case "classId":
-                            item.classId =$"{ dict[key]}";
+                            item.classId = $"{ dict[key]}";
                             break;
                         case "periodId":
                             item.periodId = $"{ dict[key]}";
@@ -53,7 +58,7 @@ namespace TEAMModelOS.SDK.Models.Service
                         default:
                             break;
                     }
-                  await  client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<Student>(item, item.id, new PartitionKey(item.code));
+                    await client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<Student>(item, item.id, new PartitionKey(item.code));
                     students.Add(item);
                 }
             }
@@ -67,9 +72,10 @@ namespace TEAMModelOS.SDK.Models.Service
         /// <param name="_azureStorage"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        public  static  async   Task FixBlobContent(CosmosClient client, DingDing _dingDing, AzureStorageFactory _azureStorage, JsonElement data)
+        public static async Task FixBlobContent(CosmosClient client, DingDing _dingDing, AzureStorageFactory _azureStorage, JsonElement data)
         {
-            if (data.TryGetProperty("doPrivate", out JsonElement _doPrivate)&& $"{_doPrivate}".Equals("yes",StringComparison.OrdinalIgnoreCase)) {
+            if (data.TryGetProperty("doPrivate", out JsonElement _doPrivate) && $"{_doPrivate}".Equals("yes", StringComparison.OrdinalIgnoreCase))
+            {
                 foreach (var cnt in _azureStorage.GetBlobServiceClient().GetBlobContainers())
                 {
                     if (cnt.Name.Length == 10 && int.TryParse(cnt.Name, out _))
@@ -79,15 +85,18 @@ namespace TEAMModelOS.SDK.Models.Service
                 }
             }
             List<School> schools = new List<School>();
-            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<School>(queryText: "select value(c) from c", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") })) {
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<School>(queryText: "select value(c) from c", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") }))
+            {
                 schools.Add(item);
             }
-            foreach (var school in schools) {
+            foreach (var school in schools)
+            {
                 await doFixBlob(client, _azureStorage, school.id, "school");
             }
         }
-        private static async Task doFixBlob(CosmosClient client, AzureStorageFactory _azureStorage,string name,string scope) {
-            List<string> prefixs = new List<string>() { "audio", "doc", "image", "other", "res", "video"};
+        private static async Task doFixBlob(CosmosClient client, AzureStorageFactory _azureStorage, string name, string scope)
+        {
+            List<string> prefixs = new List<string>() { "audio", "doc", "image", "other", "res", "video" };
             var ContainerClient = _azureStorage.GetBlobContainerClient($"{name}");
             var tb = "Teacher";
             if (scope != "private")
@@ -142,5 +151,249 @@ namespace TEAMModelOS.SDK.Models.Service
                 }
             }
         }
+
+        /// <summary>
+        /// 修復學校基本資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixSchoolPeriodId(CosmosClient client, string schoolCode)
+        {
+            List<string> periodIdList = new List<string>();
+            await foreach (School schinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<School>(queryText: $"SELECT value(c) FROM c WHERE c.id = '{schoolCode}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+            {
+                int periodIndex = 0;
+                foreach (Period periodNow in schinfo.period)
+                {
+                    if (periodNow.id.Equals("上學期") || periodNow.id.Equals("上学期") || periodNow.id.Equals("First semester"))
+                    {
+                        string periodId = Guid.NewGuid().ToString();
+                        schinfo.period[periodIndex].id = periodId;
+                        periodIdList.Add(periodId);
+                    }
+                    periodIndex++;
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(schinfo, schinfo.id, new PartitionKey("Base"));
+            }
+            return periodIdList;
+        }
+
+        /// <summary>
+        /// 修復學校班級資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixClassInfo(CosmosClient client, string schoolCode, Dictionary<string,string> dataDic)
+        {
+            List<string> classIdList = new List<string>();
+            await foreach (Class classinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Class>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            classinfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Class>(classinfo, classinfo.id, new PartitionKey($"Class-{schoolCode}"));
+                classIdList.Add(classinfo.id);
+            }
+            return classIdList;
+        }
+
+        /// <summary>
+        /// 修復學校課程資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixCourseInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> courseIdList = new List<string>();
+            await foreach (Course courseinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Course>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            courseinfo.period.id = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Course>(courseinfo, courseinfo.id, new PartitionKey($"Course-{schoolCode}"));
+                courseIdList.Add(courseinfo.id);
+            }
+            return courseIdList;
+        }
+
+        /// <summary>
+        /// 修復學校知識點資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixKnowledgeInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> knowledgeIdList = new List<string>();
+            await foreach (Knowledge knowledgeinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Knowledge>(queryText: $"SELECT value(c) FROM c WHERE c.pk = 'Knowledge' AND c.owner = '{schoolCode}'", requestOptions: new QueryRequestOptions() {}))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            knowledgeinfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Knowledge>(knowledgeinfo, knowledgeinfo.id, new PartitionKey($"{knowledgeinfo.code}"));
+                knowledgeIdList.Add(knowledgeinfo.id);
+            }
+            return knowledgeIdList;
+        }
+
+        /// <summary>
+        /// 修復學校試卷資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixPaperInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> paperIdList = new List<string>();
+            await foreach (Paper paperinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Paper>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            paperinfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Paper>(paperinfo, paperinfo.id, new PartitionKey($"Paper-{schoolCode}"));
+                paperIdList.Add(paperinfo.id);
+            }
+            return paperIdList;
+        }
+
+        /// <summary>
+        /// 修復學校課綱資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixVolumeInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> volumeIdList = new List<string>();
+            await foreach (Volume volumeinfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Volume>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            volumeinfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Volume>(volumeinfo, volumeinfo.id, new PartitionKey($"Volume-{schoolCode}"));
+                volumeIdList.Add(volumeinfo.id);
+            }
+            return volumeIdList;
+        }
+
+        /// <summary>
+        /// 修復學校試題資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixItemInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> itemIdList = new List<string>();
+            await foreach (ItemInfo iteminfo in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            iteminfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<ItemInfo>(iteminfo, iteminfo.id, new PartitionKey($"Item-{schoolCode}"));
+                itemIdList.Add(iteminfo.id);
+            }
+            return itemIdList;
+        }
+
+        /// <summary>
+        /// 修復學校評測資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixExamInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> examIdList = new List<string>();
+            await foreach (ExamInfo examinfo in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamInfo>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            examinfo.period.id = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync<ExamInfo>(examinfo, examinfo.id, new PartitionKey($"Exam-{schoolCode}"));
+                examIdList.Add(examinfo.id);
+            }
+            return examIdList;
+        }
+
+        /// <summary>
+        /// 修復學生資料
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="schoolCode"></param>
+        /// <param name="dataDic"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> FixStudentInfo(CosmosClient client, string schoolCode, Dictionary<string, string> dataDic)
+        {
+            List<string> studentIdList = new List<string>();
+            await foreach (Student studentinfo in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<Student>(queryText: $"SELECT value(c) FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{schoolCode}") }))
+            {
+                foreach (KeyValuePair<string, string> item in dataDic)
+                {
+                    switch (item.Key)
+                    {
+                        case "periodId":
+                            studentinfo.periodId = item.Value;
+                            break;
+                    }
+                }
+                await client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<Student>(studentinfo, studentinfo.id, new PartitionKey($"Base-{schoolCode}"));
+                studentIdList.Add(studentinfo.id);
+            }
+            return studentIdList;
+        }
+
     }
 }

+ 25 - 6
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/homework-feedback.less

@@ -1,6 +1,7 @@
 @import './color.less';
 
 .homework-feedback {
+  height: 100%;
   .teacher-score {
     text-align: center;
     font-size: 24px;
@@ -10,7 +11,7 @@
     }
   }
   .classmates-area {
-    height: 650px;
+    // height: 650px;
     position: relative;
     overflow: auto;
     @media screen and (max-width: 1280px) {
@@ -23,7 +24,7 @@
     box-shadow: 1px 1px 13px rgba(0, 0, 0, 0.05);
     border-radius: 7px;
     width: 100%;
-    padding: 18px 28px 40px 28px;
+    padding: 18px 28px 20px 28px;
   }
   .teacher-avatar,.classmates-avatar{
     width:30px;
@@ -41,6 +42,14 @@
     color: @secondary;
     
   }
+  .teacher-time{
+    float: right;
+    font-weight: bolder;
+    position: relative;
+    top: 5px;
+    left: -10px;
+    // color: @secondary;
+  }
   .classmates-name{
     font-weight: bolder;
     position: relative;
@@ -50,7 +59,8 @@
     
   }
   .classmates-comment {
-    margin-top: -10px;
+    // margin-top: -10px;
+    margin-bottom: 35px;
     @media screen and (max-width: 1280px) {
 
       &:first-child {
@@ -70,9 +80,9 @@
   }
   .difficulty-rate {
     position: relative;
-    top: -25px;
-    height: 20px;
-    left: -7px;
+    top: -15px;
+    height: 30px;
+    left: -5px;
     @media screen and (max-width: 1280px) {
       top: -25px;
       
@@ -82,4 +92,13 @@
   .title-rect-group {
     margin-top: 25px;
   }
+
+  .timeRate{
+    @media screen and (min-width: 1280px) {
+      margin-top: 10px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+  }
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -224,7 +224,7 @@ export default {
                             role: 'admin',
                             permission: 'notify-upd',
                             menuName: 'schoolNotify',
-                            isShow: true
+                            isShow: this.$store.state.config.srvAdr == 'China'
                         },
                         // 授权管理
                         {

+ 5 - 3
TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue

@@ -37,9 +37,11 @@
 					<DropdownItem divided class="drop-item" disabled style="cursor: text;">
 						<Icon type="logo-vimeo" class="drop-item-icon"/>
 						{{version}}
-						<br>
-						<Icon type="ios-information-circle-outline" class="drop-item-icon"/>
-						{{ $t('utils.standrad') }}:{{ curStandard ? curStandard.replace('standard','') : $t('utils.noData') }}
+                        <template v-if="$store.state.config.srvAdr === 'China'">
+                            <br>
+                            <Icon type="ios-information-circle-outline" class="drop-item-icon"/>
+                           {{ $t('utils.standrad') }}:{{ curStandard ? curStandard.replace('standard','') : $t('utils.noData') }}
+                        </template>
 					</DropdownItem>
                 </DropdownMenu>
             </Dropdown>

+ 27 - 23
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/ClassmateCommentPages.vue

@@ -320,30 +320,34 @@ export default {
 
         // 点评
         commentAndStar() {
-            this.Loading = true
-            let params = {
-                opt: "CommentAndStar",
-                id: this.homeWork.id,
-                code: this.homeWork.code.split("-")[1],
-                targetType: this.homeWork.targetType,
-                userid: this.students[this.activeIndex].userid,
-                userName: this.students[this.activeIndex].username,
-                userType: this.students[this.activeIndex].userType,
-                userSchool: this.students[this.activeIndex].userSchool,
-                comment: this.comment,
-                star: this.starNum
+            if(this.comment && this.starNum) {
+                this.Loading = true
+                let params = {
+                    opt: "CommentAndStar",
+                    id: this.homeWork.id,
+                    code: this.homeWork.code.split("-")[1],
+                    targetType: this.homeWork.targetType,
+                    userid: this.students[this.activeIndex].userid,
+                    userName: this.students[this.activeIndex].username,
+                    userType: this.students[this.activeIndex].userType,
+                    userSchool: this.students[this.activeIndex].userSchool,
+                    comment: this.comment,
+                    star: this.starNum
+                }
+                console.log(params);
+                this.$api.studentWeb.commentAndStar(params).then(res => {
+                    this.students[this.activeIndex].star = this.starNum
+                    // this.starNum = 0
+                    this.comment = ""
+                    this.Loading = false
+                    this.$Message.success("点评成功")
+                }).catch(e => {
+                    console.log(e);
+                    // this.$Message.error("点评成功")
+                })
+            } else {
+                this.$Message.warning(this.$t("studentWeb.homework.message2"))
             }
-            console.log(params);
-            this.$api.studentWeb.commentAndStar(params).then(res => {
-                this.students[this.activeIndex].star = this.starNum
-                // this.starNum = 0
-                this.comment = ""
-                this.Loading = false
-                this.$Message.success("点评成功")
-            }).catch(e => {
-                console.log(e);
-                // this.$Message.error("点评成功")
-            })
         },
         // 上一位
         preMate() {

+ 31 - 4
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Homework.vue

@@ -83,7 +83,12 @@
                             </div>
                             <div v-if="answerList.content.length">
                                 <p>附件:</p>
-                                <div class="file-show">
+                                <Table :columns="fileCol" :data="answerList.content">
+                                    <template slot-scope="{row}" slot="action">
+                                        <Icon type="md-download" size="20" @click="loadAttach(row)" :title="$t('studentWeb.homework.upload')" />
+                                    </template>
+                                </Table>
+                                <!-- <div class="file-show">
                                     <div v-for="(item, index) in answerList.content" :key="index" class="one-show">
                                         <p>{{ item.name }}</p>
                                         <p>
@@ -91,7 +96,7 @@
                                             <Icon type="md-download" size="20" @click="loadAttach(item)" :title="$t('studentWeb.homework.upload')" />
                                         </p>
                                     </div>
-                                </div>
+                                </div> -->
                             </div>
                         </div>
                         <!-- 作业上传 -->
@@ -173,8 +178,8 @@
                 </div>
             </TabPane>
             <!-- 成绩评语 -->
-            <TabPane :label="$t('studentWeb.homework.scorePage')" name="tab2" v-if="false">
-                <HomeworkFeedback />
+            <TabPane :label="$t('studentWeb.homework.scorePage')" name="tab2" v-if="answerList && isEnd">
+                <HomeworkFeedback :homeWork="homeworkInfo" />
             </TabPane>
         </Tabs>
     </div>
@@ -244,6 +249,28 @@ export default {
             isUpload: false, //是否上传
             isfinishedUpload: false, //上传成功
             maxSize: 50*1024*1024, //文件最大
+            fileCol: [
+                {
+                    title: "文件名称",
+                    key: "name",
+                    align: "center"
+                },
+                {
+                    title: "上传时间",
+                    key: "time",
+                    align: "center"
+                },
+                {
+                    title: "文件类型",
+                    key: "extension",
+                    align: "center"
+                },
+                {
+                    title: "操作",
+                    slot: "action",
+                    align: "center"
+                },
+            ],
             answerList: undefined, //以前作业列表
             isEnd: false, //时间已到,没交作业
             isApply: false, //没交作业,允许补交

+ 124 - 37
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/HomeworkFeedback.vue

@@ -9,22 +9,33 @@
                     </h2>
                 </div>
                 <div class="dec">
-                    <div class="teacher-score animate__animated animate__fadeInDown">
-                        <span class="number">95</span>
-                        <span v-if="this.$store.getters.getCurrentLaguage == 'tw'">分</span>
-                    </div>
-                    <div class="teacher-comment">
-                        <img
-                            class="teacher-avatar"
-                            src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60"
-                        />
-                        <span class="teacher-name">{{ RandomName() }}</span>
+                    <div v-if="teacherList.length">
+                        <div class="teacher-score animate__animated animate__fadeInDown">
+                            <span class="number">{{ score }}</span>
+                            <span v-if="this.$store.getters.getCurrentLaguage == 'tw'">分</span>
+                        </div>
+                        <div class="teacher-comment" v-for="(item, index) in teacherList" :key="index">
+                            <img
+                                class="teacher-avatar"
+                                src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60"
+                            />
+                            <span class="teacher-name">{{ item.username }}</span>
+                            <!-- <span class="teacher-time">{{ dateFormat(item.replies[0].time) }}</span> -->
 
-                        <p>
-                            <strong>{{ $t("studentWeb.homework.comment") }}</strong>
-                            {{ itemRandomDesc() }}
-                        </p>
+                            <p>
+                                <strong>{{ $t("studentWeb.homework.comment") }}</strong>
+                                {{ item.replies[0].comment }}
+                            </p>
+                            <div class="timeRate">
+                                <div>
+                                    评分:
+                                    <Rate class allow-half disabled v-model="item.star" />
+                                </div>
+                                <span>发布时间:{{ dateFormat(item.replies[0].time) }}</span>
+                            </div>
+                        </div>
                     </div>
+                    <div v-else>老师还没有开始打分</div>
                 </div>
             </i-col>
             <!-- 同學給分 -->
@@ -32,32 +43,46 @@
                 <div class="title-rect-group">
                     <h2 class="title-rect-name">
                         {{ $t("studentWeb.homework.classmatesComments") }}
-                        <span class="feedbackNum">30</span>
+                        <span class="feedbackNum">{{ studentList.length }}</span>
                     </h2>
                 </div>
                 <div class="dec classmates-area">
-                    <div class=" classmates-score animate__animated animate__fadeInDown">
-                        <span class="number">{{ difficultyRate.toFixed(1) }}</span>
-                        <div class="difficulty-rate">
-                            <Rate class allow-half disabled v-model="difficultyRate" />
-                        </div>
-                    </div>
-                    <div v-for="item in 30" :key="item">
-                        <div class="classmates-comment animate__animated animate__fadeInUp"
-                            :style="{ 'animation-duration': `${item * 0.2}s` }"
-                        >
-                            <img
-                                class="classmates-avatar"
-                                :src="`https://source.unsplash.com/random/300x200/?people${item}`"
-                            />
-                            <span class="classmates-name">{{ RandomName() }}</span>
-                            <p>
-                                <strong>{{ $t("studentWeb.homework.comment") }}</strong>
-                                {{ itemRandomDesc() }}
-                            </p>
+                    <div v-if="studentList.length">
+                        <div class=" classmates-score animate__animated animate__fadeInDown">
+                            <span class="number">{{ difficultyRate.toFixed(2) }}</span>
+                            <div class="difficulty-rate">
+                                <Rate class allow-half disabled v-model="difficultyRate" />
+                            </div>
                         </div>
-                        <br />
+
+                        <!-- <Scroll> -->
+                            <div v-for="(item, index) in studentList" :key="index">
+                                <div class="classmates-comment animate__animated animate__fadeInUp"
+                                    :style="{ 'animation-duration': `${index * 0.2}s` }"
+                                >
+                                    <img
+                                        class="classmates-avatar"
+                                        :src="`https://source.unsplash.com/random/300x200/?people${index}`"
+                                    />
+                                    <span class="classmates-name">{{ item.username }}</span>
+                                    <div>
+                                        <div>
+                                            <strong>{{ $t("studentWeb.homework.comment") }}</strong>
+                                            {{ item.replies[0].comment }}
+                                        </div>
+                                        <div class="timeRate">
+                                            <div>
+                                                评分:
+                                                <Rate class allow-half disabled v-model="item.star" />
+                                            </div>
+                                            <span>发布时间:{{ dateFormat(item.replies[0].time) }}</span>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        <!-- </Scroll> -->
                     </div>
+                    <div v-else>同学还没给你打分哦,再等等吧</div>
                 </div>
             </i-col>
         </Row>
@@ -68,13 +93,75 @@
 import { Random } from "mockjs"
 export default {
     name: "HomeworkFeedback",
+    props: {
+        homeWork: {
+            type: Object,
+            default: () => {undefined}
+        }
+    },
     data() {
         return {
-            difficultyRate: 4.0,
+            score: 0, //得分
+            difficultyRate: 0, //综合评分
+            studentList: [], //同学评分
+            teacherList: [], //老师评分
+        }
+    },
+    mounted () {
+        if(this.homeWork) {
+            this.getAllComment()
         }
     },
-    components: {},
     methods: {
+        // 获取同学
+        getAllComment() {
+            let roles = ""
+            if(this.$store.state.userInfo.scope == "student") {
+                roles = 'student'
+            } else {
+                roles = 'tmdId'
+            }
+            this.starNum = 0
+            let params = {
+                opt: "ReadAllComment",
+                id: this.homeWork.id,
+                code: this.homeWork.code.split("-")[1],
+                targetType: this.homeWork.targetType,
+                userid: this.$store.state.userInfo.sub,
+                userType: roles,
+                userSchool: this.$store.state.userInfo.azp
+            }
+            this.$api.studentWeb.getAllComment(params).then(res => {
+                if(res.record) {
+                    this.score = res.record.score
+                }
+                if(res.star >= 0) {
+                    this.difficultyRate = res.star
+                }
+                if(res.comments.length) {
+                    this.studentList = res.comments.filter(item => {return item.identity == "student"})
+                    this.teacherList = res.comments.filter(item => {return item.identity == "teacher"})
+                }
+            })
+        },
+        //处理时间结构
+        dateFormat(timestamp) {
+            var date = new Date(timestamp)
+            var Y = date.getFullYear() + '-'
+            var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+            var D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+            var H = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ":"
+            var Min = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
+            var S = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()) + " "
+            return Y + M + D + H + Min;
+        },
+
+
+
+
+
+
+
         RandomName() {
             return Random.name()
         },

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

@@ -226,7 +226,7 @@
                 this.isLoad = true
                 this.selectData = {}
                 this.chooseData = {}
-                let codes = this.getItemTitle.scope == 'school' ? data.school : data.creatorId
+                let codes = this.getItemTitle.scope == 'school' ? this.getItemTitle.school : this.getItemTitle.creatorId
                 if (data.blob !== undefined && data.blob !== "") {
                     let code = {
                         scope: this.paperCtn,

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/answerSheet.js

@@ -9,7 +9,7 @@ export default {
 	isShowLines: 'Sealing line',
 	objective: 'Objective Question',
 	complete: 'Cloze Question',
-	subjective: 'Short Answer Question',
+	subjective: 'Writing Question',
 	compositionZh: 'Essay(Language)',
 	compositionEn: 'Essay(English)',
 	tip1: 'Question Number',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/courseManage.js

@@ -67,7 +67,7 @@ export default {
         removeStudent2: 'Group',
         removeStuBtn: 'Apply',
         autoGroupBtn: 'Auto Group',
-        studentTableC1: 'Seat Number',
+        studentTableC1: 'Seat No.',
         studentTableC2: 'Name',
         studentTableC3: 'School System',
         studentTableC4: 'Grade',

+ 20 - 20
TEAMModelOS/ClientApp/src/locale/lang/en-US/cusMgt.js

@@ -11,8 +11,8 @@ export default {
     cNameErr:'课程名称不能包含空格',
     cusCode:'Course ID',
     codeHolder:'Please enter course ID',
-    cusDesc:'課程描述',
-    descHolder:'請輸入課程描述',
+    cusDesc:'Course Description',
+    descHolder:'Please enter course description',
     cusPd:"School System",
     pdHolder:'Please choose school system',
     cusSubject:"School System",
@@ -22,7 +22,7 @@ export default {
     delContent:'Are you sure to delete ',
     codeErr1:'Course ID cannot be empty',
     codeErr2:'Course ID can only consist of English letters and numbers',
-    tableCol1:'Serial number',
+    tableCol1:'Seat No.',
     tableCol2:'Course Name',
     tableCol3:'Course ID',
     tableCol4:'School system',
@@ -113,9 +113,9 @@ export default {
     createTips1:'Note: You (have joined a school) can select students from your school to join the course, or allow students to join the course by entering the course invitation code, scanning the course QR code, or using the invitation link.',
     createTips2:'Note: You (not yet a member of a school) can allow students to join the course by entering the course invitation code, scanning the course QR code, or using the invitation link.',
     renameListTitle:'Edit List Name',
-    selectListTips:'請選擇名單',
-    alreadyExist:'已在課程名單',
-    listAPIErr:'名單列表獲取失敗',
+    selectListTips:'Please select a list',
+    alreadyExist:' already existed',
+    listAPIErr:'Failed to get the list',
     //ManageClass.vue
     classLabel:'Class:',
     stuCount:'Student Number: ',
@@ -152,14 +152,14 @@ export default {
     groupUnit:'Group',
     stuNameList:'Student List',
     saveGroup:'Save',
-    setAvatar:'设置头像',
-    stuNameLabel:'姓名:',
-    reupload:'重新上传',
-    uploadAvatar:'上传头像',
-    uploadTips:'请上传头像',
-    setOk:'设置成功',
-    setErr:'设置失败',
-    uploadErr:'头像上传失败',
+    setAvatar:'Set Profile Picture',
+    stuNameLabel:'Name:',
+    reupload:'Re-upload',
+    uploadAvatar:'Upload Profile Picture',
+    uploadTips:'Please upload profile picture',
+    setOk:'Set up successfully',
+    setErr:'Failed to set up',
+    uploadErr:'Failed to upload profile picture',
     
     //NewCusMgt.vue
     schdTable:'Schedule Mode',
@@ -171,10 +171,10 @@ export default {
     cusTime:'Course Schedule',
     saveLabel:'Save changes',
     addStuList:'Add List',
-    addListType:'方式',
-    addListType1:'新建名单',
-    addListType2:'选择已有名单',
-    listLabel:'名单',
+    addListType:'Method',
+    addListType1:'Add a new list',
+    addListType2:'Select an existing list',
+    listLabel:'List',
     removeList:'Remove List',
     addrLabel:'Classroom:',
     nameLabel:'Class List:',
@@ -215,8 +215,8 @@ export default {
     remvTeaTitle:'Remove Instructor',
     remvTeaContent:'Are you sure to remove ',
     gradeLabel:'Grade',
-    cusNoRepeat:'课程编码重复',
-    cusNameRepeat:'课程名称重复',
+    cusNoRepeat:'The same course ID already exists',
+    cusNameRepeat:'The same course name already exists',
 
     //MgtStuList.vue
     nameList:'Name list',

+ 3 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/evaluation.js

@@ -36,9 +36,9 @@ export default {
 	typeScoreTip:'Using this allocation method, the original allocation will be reset! Do you want to continue?',
 	single:'Single Answer',
 	multiple:'Multiple Answers',
-	judge:'True-false',
+	judge:'True-False',
 	complete:'Cloze',
-	subjective:'Short Answer',
+	subjective:'Writing',
 	connector:'Matching',
 	correct:'Correcting',
 	compose:'Question Set',
@@ -208,7 +208,7 @@ export default {
 		importTips:'Import Notice',
 		tips1:' Click on the upload icon above to select a file',
 		tips2:' Only support ".docx, .xlsx, .xls" format file import, please refer to the template format to import',
-		tips3:' For now, only single answer, multiple answers, true-false, cloze, short answer, matching, correcting, and question set are supported for import',
+		tips3:' For now, only single answer, multiple answers, true-false, cloze, writing, matching, correcting, and question set are supported for import',
 		tips4:' Please keep the template language the same as the current browser language',
 		tips5:' For more details, please refer to the template making instruction',
 		warningTips1:'The upload format only supports docx/xlsx/xls , please re-upload!',

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

@@ -20,7 +20,7 @@ export default {
             value: "multiple"
         },
         {
-            label: "True-false",
+            label: "True-False",
             value: "judge"
         },
         {
@@ -28,7 +28,7 @@ export default {
             value: "complete"
         },
         {
-            label: "Short answer",
+            label: "Writing",
             value: "subjective"
         },
         {

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

@@ -47,9 +47,9 @@ export default{
     schoolLabel:'School',
     privateLabel:'Personal',
     mon:'Mon',
-    tues:'Tues',
+    tues:'Tue',
     wed:'Wed',
-    thur:'Thur',
+    thur:'Thu',
     fri:'Fri',
     sat:'Sat',
     sun:'Sun'

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/index.js

@@ -89,7 +89,7 @@ export default {
   train,
   homework,
   ability,
-  test: '测试',
+    test: 'Testing',
   formConfigP: {
     input: 'Please Enter ',
     select: 'Please Select',

+ 10 - 10
TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js

@@ -199,14 +199,14 @@ export default {
         view: 'View',
         requesting: 'Data request in progress, please wait ...',
         dataError: 'Data request abnormal, please refresh the page and try again',
-        byStuMark:'按人批阅',
-        byQuMark:'按题批阅',
-        noStuAnswer:'学生暂未作答',
-        toggleQu:'切换题目',
-        viewMark:'查看批注',
-        markLabel:'批注',
-        saveScoreTitle:'保存分数',
-        saveScoreContent:'您修改的分数尚未保存,是否需要保存?',
+        byStuMark:'Mark By People',
+        byQuMark:'Mark By Question',
+        noStuAnswer:"Students didn't answer yet",
+        toggleQu:'Switch Question',
+        viewMark:'View Annotation',
+        markLabel:'Annotation',
+        saveScoreTitle:'Save Score',
+        saveScoreContent:'The scores you modified have not been saved yet, do you want to save them?',
 
     },
 
@@ -325,8 +325,8 @@ export default {
         selectTeaTips:'請設置當前題塊的閱卷老師',
         lastQu:'題目尚未完全分配',
         reapQu:'題目劃塊設置存在重復題目',
-        objectiveLabel:'()',
-        alreadyLabel:'()',
+        objectiveLabel:'(Objective)',
+        alreadyLabel:'(Already)',
         exTag: '異常',
         scoreTips: '溫馨提示:客觀題已由系統自動評分,下面只顯示了需要人工評分的主觀題題號。 ',
         feedback:'反饋:',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/login.js

@@ -84,5 +84,5 @@ export default {
     beian1:'',
     beian2:'',
     copyright:'2021 HABOOK Group TEAM Model',
-    loginErr:'登錄信息異常,請重新登錄'
+    loginErr:'Login information is abnormal, please log in again'
 }

+ 34 - 34
TEAMModelOS/ClientApp/src/locale/lang/en-US/notify.js

@@ -1,36 +1,36 @@
 export default {
-    createTitle:'创建学校公告',
-    title:'标题',
-    titleHolder:'请输入标题',
-    notifyTime:'时间',
-    notifyContent:'内容',
-    saveText:'保存公告',
-    publishText:'发布公告',
-    cancelEdit:'取消编辑',
-    cancelPublish:'取消发布',
-    timeTips:'请设置公告时间',
-    contentTips:'请输入公告内容',
-    titleTips:'请输入公告标题',
-    reserveContent:'是否保留此次编辑?',
-    reserveOk:'保留',
-    reserveCancel:'不保留',
-    fullInfoTips:'请完善公告信息!',
-    search:'搜索',
-    draft:'草稿箱',
-    toCreate:'创建公告',
-    pending:'待发布',
-    going:'公告中',
-    finish:'已公告',
-    ntTime:'公告时间:',
-    delete:'删除',
-    view:'查看',
-    edit:'编辑',
-    noNotify:'暂无公告',
-    notifyDetail:'公告详情',
-    delNotifyTitle:'删除公告',
-    delNotifyContent:'是否确认删除',
-    yes:'是',
-    no:'',
-    delOk:'删除成功',
-    delErr:'删除失败'
+    createTitle:'Create School Announcement',
+    title:'Title',
+    titleHolder:'Please enter title',
+    notifyTime:'Time',
+    notifyContent:'Content',
+    saveText:'Save Announcement',
+    publishText:'Release Announcement',
+    cancelEdit:'Cancel Editing',
+    cancelPublish:'Cancel Announcement',
+    timeTips:'Please set the announcement time',
+    contentTips:'Please enter the announcement content',
+    titleTips:'Please enter the announcement title',
+    reserveContent:'Do you want to keep this edit?',
+    reserveOk:'Keep',
+    reserveCancel:"Don't keep",
+    fullInfoTips:'Please complete the announcement information!',
+    search:'Search',
+    draft:'Draft Box',
+    toCreate:'Create Announcement',
+    pending:'Scheduled',
+    going:'Announcing',
+    finish:'Ended',
+    ntTime:'Announcement Time:',
+    delete:'Delete',
+    view:'View',
+    edit:'Edit',
+    noNotify:'No Announcement Yet',
+    notifyDetail:'Announcement Detail',
+    delNotifyTitle:'Delete Announcement',
+    delNotifyContent:'Are you sure you want to delete',
+    ok:'Yes',
+    no:'No',
+    delOk:'Delete Successfully',
+    delErr:'Failed to delete'
 }

+ 15 - 15
TEAMModelOS/ClientApp/src/locale/lang/en-US/schoolBaseInfo.js

@@ -99,18 +99,18 @@ export default {
   semesterName: 'Name',
   semStartAt: 'Started on',
   admission: 'Enrollment Semester',
-  setStartSem:'请设置入学期',
-  semTimeTips:'请设置学期开始日期',
-  semNameTips:'学期名称不能为空',
-  semStartTips:'请设置是否为入学期',
-  gradeNameTips:'请设置年级名称',
-  examNameTips:'请设置考试类型',
-  profNameTips:'请设置专业名称',
-  anaNameTips:'请完善学情设置',
-  campNameTips:'请输入校区名称',
-  subjectNameTips:'请输入学科/专业名称',
-  delTimeTitle:'删除时段',
-  delTimeContent:'是否确认删除当前时段?',
+  setStartSem:'Please set the enrollment semester',
+  semTimeTips:'Please set the semester start date',
+  semNameTips:'Semester name cannot be empty',
+  semStartTips:'Please set whether it is the enrollment semester',
+  gradeNameTips:'Please set the grade name',
+  examNameTips:'Please set the exam type',
+  profNameTips:'Please set the professional subject name',
+  anaNameTips:'Please complete the learning status analysis setting',
+  campNameTips:'Please enter the school district name',
+  subjectNameTips:'Please enter the subject/professional subject name',
+  delTimeTitle:'Delete Schedule',
+  delTimeContent:'Are you sure you want to delete the current schedule?',
 
   // ClassroomSetting.vue
   classroomList: 'Classroom List',
@@ -186,7 +186,7 @@ export default {
   classNoErr1: 'Class ID can only contain numbers!',
   roomNoErr: 'The classroom ID cannot be empty!',
   roomNoErr1: 'Classroom ID can only be numbers or letters!',
-  roomNoErr2: 'This Classroom ID repeated',
+  roomNoErr2: 'The same classroom ID already exists',
   classAttr1: 'Regular Classroom(with fixed students)',
   classAttr2: 'Dedicated Subject Classroom (without fixed students)',
   nameWarning: 'Please enter the name',
@@ -215,7 +215,7 @@ export default {
   addErr: 'Failed to add',
   cusTabel: 'Class Schedule',
   setCusTable: 'Set Class Schedule',
-  fullRoomInfo:'请先完善教室信息!',
+  fullRoomInfo:'Please complete the classroom information first!',
 
   //ClassMgt.vue
   className:'Name',
@@ -229,7 +229,7 @@ export default {
   noSet:'Not set',
   newClass:'Create Class',
   gradeWarning1: 'Please set grade/academic year',
-  classRep: 'Same class name already exists',
+  classRep: 'The same class name already exists',
   updOk:'Modified successfully',
   updErr:'Failed to modify'
 }

+ 3 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/stuAccount.js

@@ -40,7 +40,7 @@ export default {
   adminClass:'Admin Class',
   teachClass:'Customized Class',
   stuMgt:'Student Management',
-  filterNoClass:'筛选未关联班级学生',
+  filterNoClass:'Filter students who are not associated with any classes',
   
   // AddStudent.vue
   accountInfo: 'Account Information',
@@ -100,12 +100,12 @@ export default {
   setNoErr:"Error: Seat number has been repeated within the school",
   downloadText:'(Download List Sample)',
   idRepErr:'Account already exists, will overwrite the original account',
-  stuYearErr:"Student's academic year Data is incorrect",
+  stuYearErr:"Student's academic year data is incorrect",
   classYearErr:"Class's grade level is incorrect",
   noFormatErr: '座號格式錯誤',
   classFormatErr: '班級格式錯誤',
   classNameErr: '班級名稱異常',
-  importOk: '導入成功',
+  importOk: 'Import successfully',
 
   // Authorization.vue
   authTitle: 'Service Authorization Management',

+ 5 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js

@@ -87,7 +87,7 @@ export default {
         'current-data-school': 'Current data source school',
         'Habook-smart-school': 'Habook Smarter School'
     },
-    'homeView-title': 'IES Learning Protal - AClass ONE',
+    'homeView-title': 'IES Learning Homepage - AClass ONE',
     calenderCardTitle: 'Calendar',
     recentClass: 'Recent class reminder',
     defaultRecentClass: 'Introduction to Microprocessor Basics and Applications',
@@ -287,12 +287,14 @@ export default {
         score: 'Score From Teacher',
         classmatesComments: 'Score From Classmates',
         comment: 'Give a comment: ',
+        supcomment: '补评: ',
         delTitle: "作业已被删除,是否删除此条记录?",
         delOk: "删除",
         delCancel: "取消",
         sizeOver: "该文件大小超过限制",
         extLimit: "该文件格式不符合要求",
         message1: "请先选择文件!",
+        message2: "请先进行打分和写评语",
         uploadTime: "提交时间",
         upload: "下载",
         fileSize: "文件大小",
@@ -326,9 +328,9 @@ export default {
         queType: {
             single: 'Single Answer Question',
             multiply: 'Multiple Answers Question',
-            judge: 'True-false Question',
+            judge: 'True-False Question',
             complete: 'Cloze Question',
-            subjective: 'Short Answer Question',
+            subjective: 'Writing Question',
             compose: 'Question Set',
             correct: 'Correcting Question',
             connector: 'Matching Question'

+ 4 - 4
TEAMModelOS/ClientApp/src/locale/lang/en-US/survey.js

@@ -13,8 +13,8 @@ export default {
 	addItem:'Add Question',
 	single:'Single Answer Question',
 	multiple:'Multiple Answers Question',
-	judge:'True-false Question',
-	subjective:'Short Answer Question',
+	judge:'True-False Question',
+	subjective:'Writing Question',
 	defaultName:'Default Survey Name',
 	isExistTip:'Unsaved survey activity already exists!',
 	getDataFailTip:'Failed to obtain data!',
@@ -69,8 +69,8 @@ export default {
 		confirm:'Confirm',
 		single:'Single Answer',
 		multiple:'Multiple Answers',
-		judge:'True-false',
-		subjective:'Short Answer',
+		judge:'True-False',
+		subjective:'Writing',
 		noCompleteTip:'Please fill in completely',
 		confirmTitle:'Friendly Reminder',
 		confirmText:'Are you sure to delete this question?',

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

@@ -8,7 +8,7 @@ export default {
     authErr:'Insufficient authorization!',
     development:'Features are under development, stay tuned!',
 	goHome:'Return To Home Page',
-	changePlat:'Switching Platform',
+    changePlat:'Switch Platform',
     menu:{
         school:'School',
         private:'Personal',
@@ -31,7 +31,7 @@ export default {
         scQuBack:'Question Bank',
         kdBack:'Key Concept',
         staAna:'Statistical Analysis',
-        evAna:'Learning Status Analysis',
+        evAna:'Learning Analysis',
         scAna:'Statistical Data',
         scAc:'Activity',
         scEv:'Assesment',

+ 3 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/teachContent.js

@@ -61,8 +61,8 @@ export default {
   startDown: 'Start Downloading',
   videoTips:'Friendly reminder: Only MP4 format is supported for online video playing! ',
   spaceTips: 'School Available Space = Total School Space - Space Allocated To Teachers',
-  common:'通用(未關聯學段資源)',
-  renameTitle:'重命名',
-  fileName:'文件名:'
+  common:'General',
+  renameTitle:'Rename',
+  fileName:'File Name:'
   
 }

+ 25 - 25
TEAMModelOS/ClientApp/src/locale/lang/en-US/teachermgmt.js

@@ -2,19 +2,19 @@ export default {
     page:{
         text1: 'Teacher Management',
         text2: 'Add New Teacher',
-        text3: '教研组',
+        text3: 'Pedagogical Research Team',
     },
     blurryFilter: 'Please enter ID or Name to search',
     mulitSet: 'Batch Authorization Management',
     table:{
         text1: 'Basic',
         text2: 'Advance',
-        text3: '系統管理員',
+        text3: 'System Administrator',
         th1: 'ID',
         th2: 'Name',
         th3: 'Title',
         th4: 'Authorization Status',
-        th5: '教研组',
+        th5: 'Pedagogical Research Team',
     },
     authSet:{
         title:'Authorization Settings',
@@ -146,7 +146,7 @@ export default {
             text3: 'Successfully Imported Account:',
             text4: 'Repeated Account:',
             text5: 'Failed Import Account:',
-            text6: 'ID格式錯誤',
+            text6: 'ID Format Error',
             unit: '',
             error1: 'Found repeated accounts',
             error2: 'Duplicate invitations or account not found'
@@ -161,25 +161,25 @@ export default {
     saveWarning:'Reminder',
     warningCnt:'The the current space data is not yet saved. If you leave, the modified data will not be retained!',
     leaveText:'Exit',
-    transferTitle:'轉讓管理員',
-    transferTo:'轉讓給:',
-    transferTips1:'請選擇需要將管理員身份轉讓給哪位老師? ',
-    transferTips2:'不能將管理員身份轉讓給自己',
-    trOk:'轉讓成功',
-    trErr:'轉讓失敗',
-    trOkContent:'您的管理員身份將在退出登錄後失效',
-    peopleNum:'人數:',
-    peopleUnit:'',
-    addMember:'添加成員',
-    noTeacher:'暫無老師',
-    rmvTch:'移除教師',
-    rmvTchContent:'是否確認移除',
-    yes:'',
-    no:'',
-    addOk:'添加成功',
-    addErr:'添加失敗',
-    rmvOk:'移除成功',
-    rmvErr:'移除失敗',
-    groupInfoErr:'教研組查詢失敗',
-    delGroup:'刪除教研組',
+    transferTitle:'Transfer Administratort Status',
+    transferTo:'Transfer to:',
+    transferTips1:'Please select the teacher to whom you want to transfer your administrator status?',
+    transferTips2:'You cannot transfer your administrator status to yourself',
+    trOk:'Transfer successfully',
+    trErr:'Failed to transfer',
+    trOkContent:'Your administrator status will expire after you log out',
+    peopleNum:'No. Of People:',
+    peopleUnit:'',
+    addMember:'Add Member',
+    noTeacher:'No teacher yet',
+    rmvTch:'Remove Teacher',
+    rmvTchContent:'Do you confirm remove ',
+    yes:'Yes',
+    no:'No',
+    addOk:'Add successfully',
+    addErr:'Failed to add',
+    rmvOk:'Remove successfully',
+    rmvErr:'Failed to remove',
+    groupInfoErr:'Failed to search for the group',
+    delGroup:'Delete Pedagogical Research Team',
 }

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

@@ -252,9 +252,9 @@ export default {
     ql_text1: 'Total Score',
     ql_text2: 'Single Answer Question',
     ql_text3: 'Multiple Answers Question',
-    ql_text4: 'True-false Question',
+    ql_text4: 'True-False Question',
     ql_text5: 'Cloze Question',
-    ql_text6: 'Short Answer Question',
+    ql_text6: 'Writing Question',
     ql_text7: 'Question Set',
     ql_text8: ' points',
     ql_text9: 'View Answer and Explanation',

+ 3 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/utils.js

@@ -9,10 +9,10 @@ export default {
 	draw:'Draw',
 	arrow:'Arrow',
 	circle:'Circle',
-	rect:'Rect',
+	rect:'Quadrilateral',
 	star:'Star',
-	upload:'Choose Image',
-	text:'Text input',
+	upload:'Upload Image',
+	text:'Text Input',
 	clear:'Clear',
 	undo:'Undo',
 	close:'Close',

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

@@ -287,12 +287,14 @@ export default {
         score: '教师评分',
         classmatesComments: '同学给评',
         comment: '给评: ',
+        supcomment: '补评: ',
         delTitle: "作业已被删除,是否删除此条记录?",
         delOk: "删除",
         delCancel: "取消",
         sizeOver: "该文件大小超过限制",
         extLimit: "该文件格式不符合要求",
         message1: "请先选择文件!",
+        message2: "请先进行打分和写评语",
         uploadTime: "提交时间",
         upload: "下载",
         fileSize: "文件大小",

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/cusMgt.js

@@ -22,7 +22,7 @@ export default {
     delContent: '是否確認刪除',
     codeErr1: '課程編碼不能為空',
     codeErr2: '課程編碼只能由字母和數位組成',
-    tableCol1: '號',
+    tableCol1: '號',
     tableCol2: '課程名稱',
     tableCol3: '課程編碼',
     tableCol4: '學制',

+ 14 - 14
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/jyzx.js

@@ -2,12 +2,12 @@ export default{
     common: {
         theme: "話題",
         content: "内容",
-        reply: "回",
-        comment: "評論回",
+        reply: "回",
+        comment: "評論回",
         report: "舉報",
         sure: "確定",
         cancel: "取消",
-        delete: "除",
+        delete: "除",
         complete: "已完成",
         loadTime: "上傳時間",
         action: "操作",
@@ -20,10 +20,10 @@ export default{
         pointName: "能力點名稱",
         dimension: "維度",
         message: "請選擇舉報類型:",
-        success1: "回成功",
+        success1: "回成功",
         success2: "舉報成功",
-        success3: "除成功",
-        error: "除失敗",
+        success3: "除成功",
+        error: "除失敗",
     },
     // 线上研修
     online: {
@@ -143,7 +143,7 @@ export default{
         myRecod: "我的課堂實錄",
         groupRecord: "同組課堂實錄",
         loadOK: "確認上傳",
-        videoName: "視頻名稱",
+        videoName: "影片名稱",
         fileSize: "文件大小",
         loadDes: "選擇或者拖拽檔案到該區域進行上傳",
         loadAuth: "上傳作者",
@@ -154,11 +154,11 @@ export default{
         evaResult: "評價結果",
         evaContent: "評估內容",
         evaTime: "評估時間",
-        message1: "您確實要删除這條視頻嗎?",
-        message2: "上失敗",
-        message3: "上成功",
-        message4: "除成功",
-        message5: "除失敗",
+        message1: "您確實要刪除這條影片嗎?",
+        message2: "上失敗",
+        message3: "上成功",
+        message4: "除成功",
+        message5: "除失敗",
         message6: "暫無同組課堂實錄",
     },
     // 讨论中心
@@ -169,7 +169,7 @@ export default{
         point: "能力點話題",
         allTopic: "所有話題",
         myTopic: "我的話題",
-        replyMe: "回我的",
+        replyMe: "回我的",
         placeholder1: "請輸入關鍵字",
         placeholder2: "新的話題",
         placeholder3: "請選擇",
@@ -177,7 +177,7 @@ export default{
         success1: "發表成功",
         error1: "發表失敗",
         message1: "切換至我的話題",
-        message2: "切換至回我的",
+        message2: "切換至回我的",
     },
     // 活动
     activity: {

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/knowledge.js

@@ -8,7 +8,7 @@ export default {
 	block: '知識塊',
 	knowledgeP: '請輸入知識點名稱,必填項',
 	knowledgeWarning: '知識點名稱不能為空! ',
-	knowledgeRepeat: '已存在相同名稱知識點,請勿重覆添加!',
+	knowledgeRepeat: '已存在相同名稱知識點,請勿重覆新增!',
 	newBlock: '新增知識塊',
 	addBlock: '新增知識塊',
 	inputBlock: '輸入知識塊名稱',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js

@@ -378,7 +378,7 @@ export default {
         completeQu: '當前題目已閱完,請切換題目',
         completeStu: '當前完成當前學生評分,如果繼續評分,請切換學生',
         noAnswer: '未作答',
-        stuInfoErr: '學生信息異常',
+        stuInfoErr: '學生資訊異常',
         ummarkQu: '未閱題目',
         unmarkContent: '題目尚未評分,是否跳轉到對應題目繼續評分? ',
         finished: '已閱完',

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

@@ -50,7 +50,7 @@ export default {
         error:{
             text1: '快速登入code 無效'
         },
-        text1: 'IES5智慧服務平台'
+        text1: 'IES 5智慧服務平台'
     },
     modal: {
         title: '選擇身份',
@@ -84,5 +84,5 @@ export default {
     beian1:'',
     beian2:'',
     copyright:'2021 HABOOK Group 醍摩豆',
-    loginErr:'登錄信息異常,請重新登錄'
+    loginErr:'登錄資訊異常,請重新登錄'
 }

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

@@ -14,8 +14,8 @@ export default {
     reserveContent:'是否保留此次編輯? ',
     reserveOk:'保留',
     reserveCancel:'不保留',
-    fullInfoTips:'請完善公告信息! ',
-    search:'搜',
+    fullInfoTips:'請完善公告資訊! ',
+    search:'搜',
     draft:'草稿箱',
     toCreate:'創建公告',
     pending:'待發布',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js

@@ -215,7 +215,7 @@ export default {
   addErr: '新增失敗',
   cusTabel: '課程表',
   setCusTable: '設置課表',
-  fullRoomInfo:'請先完善教室信息!',
+  fullRoomInfo:'請先完善教室資訊!',
 
   //ClassMgt.vue
   className: '名稱',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/selflearn.js

@@ -16,7 +16,7 @@ export default{
         privateContent:'個人資源',
         schoolContent:'校本資源',
         type:'類型:',
-        searchHolder:'搜',
+        searchHolder:'搜',
         quLabel:'題庫',
         testLabel:'試卷庫',
     }

+ 3 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js

@@ -249,7 +249,7 @@ export default {
         submitBVote: '提交投票',
         submitOKVote: '已投票',
         note: '請至少選擇一個選項,再進行投票! ',
-        warning: '投票失敗,請檢查投票信息! ',
+        warning: '投票失敗,請檢查投票資訊! ',
         warning2: '已超出最大投票數! ',
         voteRes: '投票結果',
         voteRecord: '投票記錄',
@@ -287,12 +287,14 @@ export default {
         score: '教師評分',
         classmatesComments: '同學給評',
         comment: '給評: ',
+        supcomment: '補評: ',
         delTitle: "作业已被删除,是否删除此條記錄?",
         delOk: "删除",
         delCancel: "取消",
         sizeOver: "該文件大小超過限制",
         extLimit: "該檔案格式不符合要求",
         message1: "請先選擇文件!",
+        message2: "請先進行打分和寫評語",
         uploadTime: "提交時間",
         upload: "下載",
         fileSize: "文件大小",

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachContent.js

@@ -59,7 +59,7 @@ export default {
   applyGrade:'適用年級',
   public:'公共資源',
   startDown:'開始下載',
-  videoTips:'溫馨提示:視頻只支持MP4格式在線播放! ',
+  videoTips:'溫馨提示:影片只支持MP4格式在線播放! ',
   spaceTips: '學校可用空間 = 學校總空間 - 分配給教師的空間',
   common:'通用(未關聯學段資源)',
   renameTitle:'重命名',

+ 4 - 4
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachermgmt.js

@@ -163,21 +163,21 @@ export default {
     leaveText:'離開',
     transferTitle:'轉讓管理員',
     transferTo:'轉讓給:',
-    transferTips1:'請選擇需要將管理員身份轉讓給哪位老師? ',
+    transferTips1:'請選擇需要將管理員身份轉讓給哪位老師?',
     transferTips2:'不能將管理員身份轉讓給自己',
     trOk:'轉讓成功',
     trErr:'轉讓失敗',
     trOkContent:'您的管理員身份將在退出登錄後失效',
     peopleNum:'人數:',
     peopleUnit:'人',
-    addMember:'添加成員',
+    addMember:'新增成員',
     noTeacher:'暫無老師',
     rmvTch:'移除教師',
     rmvTchContent:'是否確認移除',
     yes:'是',
     no:'否',
-    addOk:'添加成功',
-    addErr:'添加失敗',
+    addOk:'新增成功',
+    addErr:'新增失敗',
     rmvOk:'移除成功',
     rmvErr:'移除失敗',
     groupInfoErr:'教研組查詢失敗',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/user.js

@@ -22,7 +22,7 @@ export default{
     emailOk:'郵件發送成功',
     emailHasReg:'此郵箱已被註冊',
     emailErr:'郵件發送失敗',
-    checkTips:'請檢查信息是否正確',
+    checkTips:'請檢查資訊是否正確',
     updOk:'更新成功',
     codeErr:'驗證碼錯誤',
     nameWarning:'姓名不能為空',

+ 1 - 1
TEAMModelOS/ClientApp/src/view/teachermgmt/Index.vue

@@ -14,7 +14,7 @@
     <div class="mgmt-top">
       <div class="tab-box">
         <span class="pane" @click="paneBtn('userList')" :class="{ active: compts === 'userList' }">{{ $t('teachermgmt.page.text1') }}</span>
-        <span class="pane" @click="paneBtn('Group')" :class="{ active: compts === 'Group' }">{{ $t('teachermgmt.page.text3') }}</span>
+        <span v-if="$store.state.config.srvAdr === 'China'" class="pane" @click="paneBtn('Group')" :class="{ active: compts === 'Group' }">{{ $t('teachermgmt.page.text3') + '23232' }}</span>
         <span v-if="$access.can('admin.*|teacher-upd')" class="pane" @click="paneBtn('personnel')" :class="{ active: compts === 'personnel' }">{{ $t('teachermgmt.page.text2') }} <Badge :count="requestedUserCount" class-name="badgesty"></Badge></span>
       </div>
     </div>

+ 253 - 6
TEAMModelOS/Controllers/Client/HiTAControlller.cs

@@ -1,13 +1,21 @@
+using Azure.Cosmos;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
+using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
+using System.Dynamic;
+using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
+using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.Services.Common;
+using static TEAMModelOS.Services.Common.BlobService;
 
 namespace TEAMModelOS.Controllers.Client
 {
@@ -15,32 +23,271 @@ namespace TEAMModelOS.Controllers.Client
     [ApiController]
     public class HiTAControlller : ControllerBase
     {
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly AzureCosmosFactory _azureCosmos;
         private readonly DingDing _dingDing;
         private readonly Option _option;
+        private readonly SnowflakeId _snowflakeId;
 
-        public HiTAControlller(DingDing dingDing, IOptionsSnapshot<Option> option)
+        public HiTAControlller(
+            AzureStorageFactory azureStorage,
+            AzureRedisFactory azureRedis,
+            AzureCosmosFactory azureCosmos,
+            DingDing dingDing,
+            SnowflakeId snowflakeId,
+            IOptionsSnapshot<Option> option)
         {
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
+            _azureCosmos = azureCosmos;
             _dingDing = dingDing;
+            _snowflakeId = snowflakeId;
             _option = option?.Value;
         }
 
-        [Authorize(Roles = "HiTeach")]
+        //[Authorize(Roles = "HiTA")]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status400BadRequest)]
         [ProducesDefaultResponseType]
-        [HttpPost("init")]
-        public async Task<IActionResult> Init()
+        [HttpPost("get-teacher-info")]
+        public async Task<IActionResult> GetTeacherInfo()
         {
             try
             {
+                string id_token = HttpContext.GetXAuth("IdToken");
+                if (string.IsNullOrEmpty(id_token)) return BadRequest();
+                var jwt = new JwtSecurityToken(id_token);
+                if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
+                var id = jwt.Payload.Sub;
 
+                
+                var clientc = _azureCosmos.GetCosmosClient();
+                var clientr = _azureRedis.GetRedisClient(8);
+                var response = await clientc.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
+                if (response.Status == 200)
+                {
+                    var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
+                    //學校資訊
+                    List<dynamic> schools = new List<dynamic>();
+                    if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
+                    {
+                        foreach (var obj in value.EnumerateArray())
+                        {
+                            string schoolCodeNow = $"{obj.GetProperty("schoolId")}";
+                            var clientss = _azureStorage.GetBlobContainerClient(schoolCodeNow);
+                            //Blob使用狀況
+                            UsedBlob schoolUsedBlob = await BlobService.GetBlobUsed(clientc, clientss, clientr, "school", schoolCodeNow);
+                            //管理者名單
+                            List<Dictionary<string, string>> adminList = new List<Dictionary<string, string>>();
+                            string querySchool = $"SELECT c.id, c.name FROM c WHERE ARRAY_CONTAINS(c.roles, 'admin', true)";
+                            await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(querySchool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{schoolCodeNow}") }))
+                            {
+                                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                {
+                                    foreach (var objadmin in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                    {
+                                        Dictionary<string, string> adminRow = new Dictionary<string, string>();
+                                        adminRow.Add("id", objadmin.GetProperty("id").GetString());
+                                        adminRow.Add("name", objadmin.GetProperty("name").GetString());
+                                        adminList.Add(adminRow);
+                                    }
+                                }
+                            }
+                            //老師在此學校的課程
+                            List<dynamic> coursesch = new List<dynamic>();
+                            var query = $"SELECT c.id, c.name, c.no FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
+                            await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{schoolCodeNow}") }))
+                            {
+                                var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
+                                if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                {
+                                    foreach (var obj2 in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
+                                    {
+                                        dynamic courseExtobj = new ExpandoObject();
+                                        courseExtobj.id = obj2.GetProperty("id");
+                                        courseExtobj.name = obj2.GetProperty("name");
+                                        courseExtobj.no = obj2.GetProperty("no");
+                                        coursesch.Add(courseExtobj);
+                                    }
+                                }
+                            }
+                           
+                            //學校資料生成
+                            dynamic schoolExtobj = new ExpandoObject();
+                            var schoolJson = await clientc.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
+                            var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
+                            schoolExtobj.schoolId = obj.GetProperty("schoolId");
+                            schoolExtobj.name = school.RootElement.GetProperty("name");
+                            schoolExtobj.status = obj.GetProperty("status");
+                            schoolExtobj.picture = school.RootElement.GetProperty("picture");
+                            schoolExtobj.size = new ExpandoObject();
+                            schoolExtobj.size.used = schoolUsedBlob.size + schoolUsedBlob.teach * 1073741824;
+                            long ssize = (school.RootElement.TryGetProperty("size", out JsonElement ssizeJson)) ? ssizeJson.GetInt32() : 0;
+                            schoolExtobj.size.total = ssize * 1073741824;
+                            schoolExtobj.size.avaliable = schoolExtobj.size.total - schoolExtobj.size.used;
+                            schoolExtobj.admin = adminList;
+                            schoolExtobj.courses = coursesch;
+
+                            schools.Add(schoolExtobj);
+                        }
+                    }
+                    //預設學校
+                    string defaultschool = null;
+                    if(jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement defaultSchoolJson) && !defaultSchoolJson.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        defaultschool = defaultSchoolJson.GetString();
+                    }
+                    //老師個人課程
+                    List<dynamic> courses = new List<dynamic>();
+                    await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.no FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
+                    {
+                        var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
+                        if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                dynamic courseExtobj = new ExpandoObject();
+                                courseExtobj.id = obj.GetProperty("id");
+                                courseExtobj.name = obj.GetProperty("name");
+                                courseExtobj.no = obj.GetProperty("no");
+                                courses.Add(courseExtobj);
+                            }
+                        }
+                    }
+                    //老師個人空間  ※老師可用的空間 = (Teacher)Base.size + (School)各校的Teacher-{schoolCode}
+                    dynamic size = new ExpandoObject();
+                    size.resource = 0;
+                    size.records = 0;
+                    size.used = 0;
+                    size.total = 0;
+                    var clientst = _azureStorage.GetBlobContainerClient(id);
+                    ////個人可用空間
+                    if (jsonsc.RootElement.TryGetProperty("size", out JsonElement teaSizeTotalJson) && teaSizeTotalJson.ValueKind.Equals(JsonValueKind.Number))
+                    {
+                        size.total += teaSizeTotalJson.GetInt32();
+                    }
+                    ////學校分派給老師的空間
+                    int GsizeFromSchool = 0;
+                    string querySizeFromSchool = $"SELECT sum(c.size) as size FROM c WHERE c.pk = 'Teacher' AND c.id = {id}";
+                    await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(querySizeFromSchool, requestOptions: new QueryRequestOptions() { }))
+                    {
+                        var json = await JsonDocument.ParseAsync(item.ContentStream);
+                        foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            if (elmt.TryGetProperty("size", out JsonElement sizeJson) && sizeJson.ValueKind.Equals(JsonValueKind.Number))
+                            {
+                                GsizeFromSchool = sizeJson.GetInt32();
+                                break;
+                            }
+                        }
+                    }
+                    size.total += GsizeFromSchool;
+                    size.total = Convert.ToInt64(size.total) * 1073741824;
+
+                    ////個人空間使用狀況
+                    UsedBlob teacherUsedBlob = await BlobService.GetBlobUsed(clientc, clientst, clientr, "private", id);
+                    size.used = teacherUsedBlob.size;
+                    foreach (KeyValuePair<string, double?> blobCatalog in teacherUsedBlob.catalog)
+                    {
+                        if(blobCatalog.Key.Equals("records"))
+                        {
+                            size.records = blobCatalog.Value;
+                        }
+                        else
+                        {
+                            size.resource += blobCatalog.Value;
+                        }
+                    }
+                    
+
+                    //資源數
+                    int resCount = 0; //教材數
+                    int itemCount = 0; //題目數
+                    int paperCount = 0; //試卷數
+                    List<BlobCount> blogCountList = await BloblogCount(clientc, "private", id, "", "");
+                    foreach(BlobCount blobCountRow in blogCountList)
+                    {
+                        switch (blobCountRow.type)
+                        {
+                            case "item":
+                                itemCount += blobCountRow.count;
+                                break;
+                            case "paper":
+                                paperCount += blobCountRow.count;
+                                break;
+                            case "doc":
+                            case "image":
+                            case "res":
+                            case "video":
+                            case "audio":
+                            case "other":
+                                resCount += blobCountRow.count;
+                                break;
+                        }
+                    }
+
+                    //進行中的活動數
+                    int activityCount = 0;
+                    List<string> examIdList = new List<string>();
+                    await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                activityCount += obj.GetProperty("count").GetInt32();
+                            }
+                        }
+                    }
+                    await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Vote-{id}") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                activityCount += obj.GetProperty("count").GetInt32();
+                            }
+                        }
+                    }
+                    await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{id}") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                activityCount += obj.GetProperty("count").GetInt32();
+                            }
+                        }
+                    }
+                    await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{id}") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                activityCount += obj.GetProperty("count").GetInt32();
+                            }
+                        }
+                    }
+
+                    return Ok(new { schools, defaultschool, courses, size, resCount, itemCount, paperCount, activityCount });
+                }
+                else //無此老師
+                {
+                    return BadRequest();
+                }
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},Channel/Create()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                //await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},Channel/Create()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
-            return Ok();
         }
     }
 }

+ 64 - 18
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -930,7 +930,7 @@ namespace TEAMModelOS.Controllers.Client
             if (grant_type.GetString() .Equals("school") && string.IsNullOrWhiteSpace(Convert.ToString(school_code))) return BadRequest();
 
             var client = _azureCosmos.GetCosmosClient();
-            List<object> students = new List<object>();
+            List<ClassStudents> students = new List<ClassStudents>();
             string container = (grant_type.GetString() .Equals("school")) ? "School" : "Teacher";
             //Case 1 取得stulist成員 (有stulist_id則優先取)
             if (grant_type.GetString().Equals("private")  && string.IsNullOrWhiteSpace(stulist) && !string.IsNullOrWhiteSpace(classId)) //若private,且classId不為空,stulist為空,有可能HiTeach無法判別是若classId還是stulist
@@ -989,14 +989,15 @@ namespace TEAMModelOS.Controllers.Client
                             {
                                 foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
                                 {
-                                    students.Add(stuObj.ToObject<object>());
+                                    students.Add(stuObj.ToObject<ClassStudents>());
                                 }
                             }   
                         }
                     }
                 }
                 //取得TMID用戶資料
-                if(tmidList.Count > 0)
+                List<ClassStudents> tmids = new List<ClassStudents>();
+                if (tmidList.Count > 0)
                 {
                     var querytmct = $"SELECT c.id, c.name, null AS no, null AS schoolId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(tmidList)}, c.id)";
                     await foreach (var itemtm in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: querytmct, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
@@ -1006,11 +1007,14 @@ namespace TEAMModelOS.Controllers.Client
                         {
                             foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
                             {
-                                students.Add(stuObj.ToObject<object>());
+                                tmids.Add(stuObj.ToObject<ClassStudents>());
                             }
+                            //排序
+                            tmids = tmids.OrderBy(x => tmidList.IndexOf(x.id)).ToList();
                         }
                     }
                 }
+                students = students.Concat(tmids).ToList();
             }
             //Case 2 取得班級固定成員 條件:有school_code、有classId、無stulist
             if (!string.IsNullOrWhiteSpace(Convert.ToString(school_code)) && students.Count == 0 && !string.IsNullOrWhiteSpace(classId))
@@ -1023,7 +1027,7 @@ namespace TEAMModelOS.Controllers.Client
                     {
                         foreach (var stuObj in jsonst.RootElement.GetProperty("Documents").EnumerateArray())
                         {
-                            students.Add(stuObj.ToObject<object>());
+                            students.Add(stuObj.ToObject<ClassStudents>());
                         }
                     }
                 }
@@ -1083,6 +1087,7 @@ namespace TEAMModelOS.Controllers.Client
 
                 if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
                 if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
+                bool blobUploaded = (request.TryGetProperty("blobUploaded", out JsonElement blobUploadedJson)) ? blobUploadedJson.GetBoolean() : false;
 
                 ExamInfo ExamInfoFromReq = exam.ToObject<ExamInfo>();
                 string examId = ExamInfoFromReq.id;
@@ -1105,7 +1110,7 @@ namespace TEAMModelOS.Controllers.Client
                     dbExamInfo = ExamInfoFromReq;
                 }
 
-                Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
+                //Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
                 //ExamInfo內容取得、調整  [2021-7-13 廢除,給予HiTeach學校Blob寫入權限,API不再對Blob做搬運]
                 //※規則  owner:"school" => 校園評測  "teacher" => 個人評測 
                 //※規則  scope:"school" => 校本班級  "private" => 個人班級 
@@ -1113,18 +1118,23 @@ namespace TEAMModelOS.Controllers.Client
                 string blobContainer = (!string.IsNullOrWhiteSpace(dbExamInfo.school) && dbExamInfo.scope .Equals("school")) ? dbExamInfo.school : id; //blob容器
                 //dbExamInfo.source = "1"; //評測來源固定為  1:課中評量(不應由API擅自變更)
 
+                //試卷List
+                List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
+                int paperIndex = 0;
+                foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
+                {
+                    string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
+                    paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
+                    string subjectId = dbExamInfo.subjects[paperIndex].id;
+                    recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "subjectId", subjectId }, { "itemcount", paperInfo.point.Count.ToString() } });
+                    paperIndex++;
+                }
+
                 //取得課堂紀錄下的試卷資料(blob)、複製到評測紀錄下
                 bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag  true:拷貝錯誤  [2021-7-13 (測試站)不再對Blob做搬運 永為false]
-                //非測試站執行以下程序 (待HiTeach完善後刪除)
-                if (!isTestFlg)
+                //Blob搬運 (待HiTeach完善後刪除)
+                if (!blobUploaded)
                 {
-                    List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
-                    foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
-                    {
-                        string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
-                        paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
-                        recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath } });
-                    }
                     foreach (Dictionary<string, string> recordPaperInfoDic in recordPaperInfo)
                     {
                         string targetScope = dbExamInfo.scope; //評測對象 school:校本班級  private:私人課程
@@ -1186,8 +1196,8 @@ namespace TEAMModelOS.Controllers.Client
 
                 //ExamClassResult內容調整
                 bool studentAnswerCopyErrFlg = false; //學生作答資料拷貝錯誤Flag  true:拷貝錯誤 [2021-7-13 不再對Blob做搬運 永為false]
-                //非測試站執行以下程序 (待HiTeach完善後刪除)
-                if (!isTestFlg)
+                //Blob搬運 (待HiTeach完善後刪除)
+                if (!blobUploaded)
                 {
                     List<ExamClassResultStudentAnswerArrayOld> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArrayOld>>();
                     foreach (ExamClassResultStudentAnswerArrayOld examClassResultRow in dbExamClassResultList)
@@ -1233,7 +1243,6 @@ namespace TEAMModelOS.Controllers.Client
                             examClassResultUpd.scope = examClassResultRow.scope;
                             examClassResultUpd.sum = examClassResultRow.sum;
                         }
-
                         //examClassResult.studentAnswers 將學生答案上傳blob後轉換內容為blob路徑   //[2021-7-13 不再對Blob做搬運]
                         if (examClassResultRow.studentIds != null && examClassResultRow.studentIds.Count > 0 && examClassResultRow.studentAnswersArray != null && examClassResultRow.studentAnswersArray.Count > 0)
                         {
@@ -1252,6 +1261,13 @@ namespace TEAMModelOS.Controllers.Client
                                 examClassResultUpd.studentAnswers.Add(studenrAnswerRow);
                             }
                         }
+                        //批註欄位處理
+                        Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string paperStatusSubjectId) && paperStatusSubjectId.Equals(subjectId)).FirstOrDefault();
+                        int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
+                        if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
+                        {
+                            examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
+                        }
 
                         //UPDATE ExamClassResult
                         var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
@@ -1304,6 +1320,13 @@ namespace TEAMModelOS.Controllers.Client
                             examClassResultUpd.sum = examClassResultRow.sum;
                         }
                         examClassResultUpd.studentAnswers = examClassResultRow.studentAnswersArray; //[2021-7-13 不再對Blob做搬運 學生答案直接寫入DB]
+                        //批註欄位處理
+                        Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string subjectId) && subjectId.Equals(examClassResultUpd.subjectId)).FirstOrDefault();
+                        int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
+                        if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
+                        {
+                            examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
+                        }
 
                         //UPDATE ExamClassResult
                         var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
@@ -1339,6 +1362,21 @@ namespace TEAMModelOS.Controllers.Client
             }
         }
 
+        private List<List<List<Details>>> createEmptyMark(int studentNum, int itemNum)
+        {
+            List<List<List<Details>>> mark = new List<List<List<Details>>>();
+            for (int i = 0; i < studentNum; i++)
+            {
+                List<List<Details>> markRow = new List<List<Details>>();
+                for (int j = 0; j < itemNum; j++)
+                {
+                    markRow.Add(new List<Details>());
+                }
+                mark.Add(markRow);
+            }
+            return mark;
+        }
+
         //課綱的model先記在下面,待式樣確定後再轉換
         private List<SyllabusNode> CreateSyllabusTree(List<Syllabus> syllabuses)
         {
@@ -1384,6 +1422,14 @@ namespace TEAMModelOS.Controllers.Client
             public object structure { get; set; }
         }
 
+        public class ClassStudents
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public string no { get; set; }
+            public string schoolId { get; set; }
+        }
+
         public class Syllabus
         {
             public string id { get; set; }

+ 31 - 31
TEAMModelOS/Controllers/Client/SokrateController.cs

@@ -52,50 +52,50 @@ namespace TEAMModelOS.Controllers.Client
 
         [AllowAnonymous]
         [ProducesDefaultResponseType]
-        [HttpPost("get-school-blob-read")]
-        public async Task<IActionResult> GetSchoolBlobRead(JsonElement request)
+        [HttpPost("get-blob-read")]
+        public async Task<IActionResult> GetBlobRead(JsonElement request)
         {
             //Debug
             //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
             try
             {
-                string service_token = HttpContext.GetXAuth("ServiceToken");
-                if (string.IsNullOrWhiteSpace(service_token)) return BadRequest();
-                if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
+                string id_token = HttpContext.GetXAuth("IdToken");
+                if (string.IsNullOrEmpty(id_token)) return BadRequest();
+                var jwt = new JwtSecurityToken(id_token);
+                if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
+                var id = jwt.Payload.Sub;
+                string school_code = (request.TryGetProperty("school_code", out JsonElement schoolCodeJson) && !schoolCodeJson.ValueKind.Equals(JsonValueKind.Null)) ? schoolCodeJson.GetString() : string.Empty;
+                string teacher_blob_uri_read = string.Empty;
+                string teacher_blob_sas_read = string.Empty;
+                string school_blob_uri_read = string.Empty;
+                string school_blob_sas_read = string.Empty;
+
                 var client = _azureCosmos.GetCosmosClient();
-                string location = string.Empty;
-                if (_option.Location.Contains("China"))
-                {
-                    location = "China";
-                }
-                else if (_option.Location.Contains("Global"))
+                //取得老師blob sas
+                var responsetea = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
+                if (responsetea.Status == 200)
                 {
-                    location = "Global";
+                    (teacher_blob_uri_read, teacher_blob_sas_read) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Read); //讀
                 }
-                //驗證ServiceToken
-                string jsonServiceToken = Encoding.UTF8.GetString(Convert.FromBase64String(service_token));
-                ServiceClientInfo ServiceClientInfo = JsonSerializer.Deserialize<ServiceClientInfo>(jsonServiceToken);
-                AuthenticationResult token = await CoreTokenExtensions.CreateAccessToken(ServiceClientInfo.client_id, ServiceClientInfo.client_secret, location);
-                var jwt = new JwtSecurityToken(token.AccessToken);
-                if (CoreTokenExtensions.ValidateAccessToken(jwt))
+                //取得學校blob sas
+                if (!String.IsNullOrWhiteSpace(school_code))
                 {
-                    string school_code_blob = school_code.GetString().ToLower();
-                    //檢測取得學校基本資料
-                    var responsesch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code_blob, new PartitionKey($"Base"));
+                    string school_code_blob = school_code.ToLower();
+                    var responsesch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code_blob, new PartitionKey("Base"));
                     if (responsesch.Status == 200)
                     {
-                        var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
-                        return Ok(new { blob_sas_read });
-                    }
-                    else
-                    {
-                        return BadRequest();
+                        (school_blob_uri_read, school_blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
                     }
                 }
-                else
-                {
-                    return BadRequest();
-                }
+
+                return Ok(new { teacher_blob_sas_read, school_blob_sas_read });
+
+                //驗證ServiceToken
+                //string jsonServiceToken = Encoding.UTF8.GetString(Convert.FromBase64String(service_token));
+                //ServiceClientInfo ServiceClientInfo = JsonSerializer.Deserialize<ServiceClientInfo>(jsonServiceToken);
+                //AuthenticationResult token = await CoreTokenExtensions.CreateAccessToken(ServiceClientInfo.client_id, ServiceClientInfo.client_secret, location);
+                //var jwt = new JwtSecurityToken(token.AccessToken);
+                //if (CoreTokenExtensions.ValidateAccessToken(jwt)) {}
             }
             catch (Exception ex)
             {

+ 137 - 18
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -632,7 +632,7 @@ namespace TEAMModelOS.Controllers
                 ExamInfo info = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(id.GetString(), new PartitionKey($"{scode}"));
                 //确定当前学生所在班级且该班级参与该活动
                 List<string> cIds = new();
-                if (ids.Count == 0) return Ok(new { code = 404,msg = "未找到相关班级信息"});
+                if (ids.Count == 0) return Ok(new { code = 404, msg = "未找到相关班级信息" });
                 foreach (string cId in ids)
                 {
                     //List<string> stus = new List<string>();
@@ -1263,8 +1263,8 @@ namespace TEAMModelOS.Controllers
                 if (!requert.TryGetProperty("code", out JsonElement school)) return BadRequest();
                 if (!requert.TryGetProperty("scode", out JsonElement scode)) return BadRequest();
                 var client = _azureCosmos.GetCosmosClient();
-                var query = $"select c.id,c.code,c.school,c.creatorId,c.progress,A0.id paperId,A0.code paperCode,A0.name paperName,A0.multipleRule,A0.scope,A0.blob from c join A0 in c.papers where c.id ='{id}'";
-                List<object> papers = new List<object>();
+                var query = $"select c.id,c.code,c.school,c.creatorId,c.progress,A0.id paperId,A0.code paperCode,A0.name paperName,A0.knowledge,A0.point,A0.field,A0.multipleRule,A0.scope,A0.blob from c join A0 in c.papers where c.id ='{id}'";
+                List<PaperSimple> papers = new List<PaperSimple>();
                 List<object> subjects = new List<object>();
                 List<string> classIds = new List<string>();
                 //List<string> stus = new List<string>();
@@ -1337,19 +1337,15 @@ namespace TEAMModelOS.Controllers
                         infoIds.Add(ids);
                     }
                 }
-                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{scode}") }))
+
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<PaperSimple>(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{scode}") }))
                 {
-                    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())
-                        {
-                            papers.Add(obj.ToObject<object>());
-                        }
-                    }
+                    papers.Add(item);
                 }
+
                 var querySubject = $"select A0.id,A0.name from c join A0 in c.subjects where c.id ='{id}'";
                 //List<object> props = new List<object>();
+                List<string> subId = new();
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querySubject, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{scode}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -1358,10 +1354,11 @@ namespace TEAMModelOS.Controllers
                         foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                         {
                             subjects.Add(obj.ToObject<object>());
+                            subId.Add(obj.GetProperty("id").GetString());
                         }
                     }
                 }
-                var queryAnswers = $"select c.id,c.code,c.studentIds,c.subjectId,c.studentAnswers,c.studentScores,c.sum,c.mark from c where c.examId ='{id}' and array_contains(c.studentIds,'{studentId}') and c.pk = 'ExamClassResult' ";
+                var queryAnswers = $"select c.id,c.code,c.studentIds,c.subjectId,c.studentAnswers,c.studentScores,c.sum,c.mark,c.krate,c.frate from c where c.examId ='{id}' and array_contains(c.studentIds,'{studentId}') and c.pk = 'ExamClassResult' ";
                 List<ExamClassResult> answers = new List<ExamClassResult>();
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: queryAnswers,
                     requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school}") }))
@@ -1396,11 +1393,23 @@ namespace TEAMModelOS.Controllers
                         total.Add(result.sum.Where(s => s > 70 && s <= 80).Count());
                         total.Add(result.sum.Where(s => s > 80 && s <= 90).Count());
                         total.Add(result.sum.Where(s => s > 90 && s <= 100).Count());
+
+
                     }
                 }
+                List<(List<string> kn, List<double> kps, List<double> ckps)> know = new();
+                List<(List<int> fs, List<double> fps, List<double> cfps)> fp = new();
+                foreach (var s in stuTask(subId, papers, answers, studentId.GetString()))
+                {
+                    know = s.know;
+                    fp = s.filed;
+                }
                 if (papers.IsNotEmpty())
                 {
-                    return Ok(new { papers, subjects, stuScore, stuAns, mark, total, claId = infoIds, status = 200 });
+                    var knowledge = know.Select(k => new { k.kn, k.kps,k.ckps });
+                    var filed = fp.Select(k => new { k.fs, k.fps,k.cfps });
+                    //papers = papers
+                    return Ok(new { papers, subjects, stuScore, stuAns, mark, total, claId = infoIds, knowledge, filed, status = 200 });
                 }
                 else
                 {
@@ -1415,6 +1424,113 @@ namespace TEAMModelOS.Controllers
             }
 
         }
+        private IEnumerable<(List<(List<string> kn, List<double> kps, List<double> ckps)> know, List<(List<int> fs, List<double> fps, List<double> cfps)> filed)> stuTask(List<string> subjectIds, List<PaperSimple> papers, List<ExamClassResult> answers, string stuId)
+        {
+            List<(List<string> kn, List<double> kps, List<double> ckps)> know = new List<(List<string> kn, List<double> kps, List<double> ckps)>();
+            List<(List<int> fs, List<double> fps, List<double> cfps)> filed = new List<(List<int> fs, List<double> fps, List<double> cfps)>();
+            int no = 0;
+            foreach (string sub in subjectIds)
+            {
+                List<double> kp = new();
+                List<double> fp = new();
+                List<double> ckp = new();
+                List<double> cfp = new();
+                List<int> fields = new() { 1, 2, 3, 4, 5, 6 };
+                //获取该试卷所有知识点和认知层次分布
+                List<List<string>> kones = papers[no].knowledge;
+                List<int> fs = papers[no].field;
+                List<double> point = papers[no].point;
+                //去重知识点
+                HashSet<string> knowles = new();
+                List<string> knowledges = new();
+                if (kones.Count > 0)
+                {
+                    kones.ForEach(kno =>
+                    {
+                        kno.ForEach(k =>
+                        {
+                            knowles.Add(k);
+                        });
+                    });
+                }
+                foreach (string cla in knowles)
+                {
+                    knowledges.Add(cla);
+                }
+                for (int k = 0; k < knowledges.Count; k++)
+                {
+                    if (null == knowledges[k])
+                    {
+                        knowledges.Remove(knowledges[k]);
+                    }
+                }
+                foreach (ExamClassResult result in answers)
+                {
+                    if (result.subjectId.Equals(sub))
+                    {
+                        int index = result.studentIds.IndexOf(stuId.ToString());
+                        //处理知识点
+                        foreach (string k in knowledges)
+                        {
+                            //初始化单个知识点得分
+                            double score = 0;
+                            double allScore = 0;
+                            int n = 0;
+                            foreach (List<string> str in kones)
+                            {
+                                if (str.Contains(k))
+                                {
+                                    var itemPersent = str.Count > 0 ? 1 / Convert.ToDouble(str.Count) : 0;
+                                    allScore += point.Count > 0 ? point[n] * itemPersent : 0;
+
+                                    if (result.studentScores.Count > 0)
+                                    {
+                                        if (result.studentScores[index].Count > 0)
+                                        {
+                                            score += result.studentScores[index][n] == -1 ? 0 : result.studentScores[index][n];
+                                        }
+                                    }
+                                }
+                                n++;
+                            }
+                            kp.Add(allScore > 0 ? Math.Round(score / allScore, 2) : 0);
+                        }
+                        //处理认知层次
+                        foreach (int k in fields)
+                        {
+                            //初始化认知层次得分
+                            double score = 0;
+                            double allScore = 0;
+                            int n = 0;
+                            foreach (int str in fs)
+                            {
+                                if (str == k)
+                                {
+                                    var itemPersent = 1;
+                                    allScore += point.Count > 0 ? point[n] * itemPersent : 0;
+
+                                    if (result.studentScores.Count > 0)
+                                    {
+                                        if (result.studentScores[index].Count > 0)
+                                        {
+                                            score += result.studentScores[index][n] == -1 ? 0 : result.studentScores[index][n];
+                                        }
+                                    }
+                                }
+                                n++;
+                            }
+                            fp.Add(allScore > 0 ? Math.Round(score / allScore ,2): 0);
+                        }
+                        ckp = result.krate;
+                        cfp = result.frate;
+                    }
+                }
+                know.Add((knowledges, kp,ckp));
+                filed.Add((fields, fp,cfp));
+                no++;
+            }
+            yield return (know, filed);
+        }
 
         //查询学生活动列表
         [ProducesDefaultResponseType]
@@ -2633,11 +2749,14 @@ namespace TEAMModelOS.Controllers
                     //阅卷老师
                     List<object> teachers = new();
                     var cResponse = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemStreamAsync(id.ToString(), new PartitionKey($"Correct-{code}"));
-                    if (cResponse.Status == 200) {
+                    if (cResponse.Status == 200)
+                    {
                         using var cJson = await JsonDocument.ParseAsync(response.ContentStream);
                         Correct correct = cJson.ToObject<Correct>();
-                        foreach (CorSub corSub in correct.subs) {
-                         var obj = corSub.markers.Select(s => new { 
+                        foreach (CorSub corSub in correct.subs)
+                        {
+                            var obj = corSub.markers.Select(s => new
+                            {
                                 sId = corSub.id,
                                 sName = corSub.name,
                                 name = s.name,
@@ -2652,7 +2771,7 @@ namespace TEAMModelOS.Controllers
 
                     }
 
-                        return Ok(new { sc ,teachers});
+                    return Ok(new { sc, teachers });
                 }
                 else
                 {

+ 1 - 1
TEAMModelOS/Controllers/School/StudentController.cs

@@ -1437,7 +1437,7 @@ namespace TEAMModelOS.Controllers
                         classId = item.Key;
                         className = classInfo[item.Key].GetProperty("name").GetString();
                         classNo = classInfo[item.Key].GetProperty("no").GetString();
-                        gradeId = classInfo[item.Key].GetProperty("gradeId").GetString();
+                        //gradeId = classInfo[item.Key].GetProperty("gradeId").GetString(); 此欄位已不使用
                         periodId = classInfo[item.Key].GetProperty("periodId").GetString();
                     }
                     else if (item.Key.Length == 0)

+ 54 - 0
TEAMModelOS/Controllers/XTest/FixDataController.cs

@@ -392,5 +392,59 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
+
+        /// <summary>
+        /// 修正學段ID非英數邏輯
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "teacher")]
+        [HttpPost("fix-periodid")]
+        public async Task<IActionResult> FixPeriodid(JsonElement data)
+        {
+            try
+            {
+                if (!data.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
+                string schoolCode = school_code.GetString();
+                var client = _azureCosmos.GetCosmosClient();
+                //學校 Base
+                List<string> periodList = await FixDataService.FixSchoolPeriodId(client, schoolCode);
+                if (periodList.Count > 0)
+                {
+                    string periodId = periodList[0]; //修正的問題校都只有一個學段,以第一個學段ID為其餘資料的學段ID
+                    Dictionary<string, string> dataDic = new Dictionary<string, string>();
+                    dataDic["periodId"] = periodId;
+                    //班級 Class
+                    List<string> fixClassIdList = await FixDataService.FixClassInfo(client, schoolCode, dataDic);
+                    //課程 Course
+                    List<string> fixCourseIdList = await FixDataService.FixCourseInfo(client, schoolCode, dataDic);
+                    //知識點 Knowledge
+                    List<string> fixKnowledgeIdList = await FixDataService.FixKnowledgeInfo(client, schoolCode, dataDic);
+                    //試題 Item
+                    List<string> fixItemIdList = await FixDataService.FixItemInfo(client, schoolCode, dataDic);
+                    //試卷 Paper
+                    List<string> fixPaperIdList = await FixDataService.FixPaperInfo(client, schoolCode, dataDic);
+                    //課綱 Volume
+                    List<string> fixVolumeIdList = await FixDataService.FixVolumeInfo(client, schoolCode, dataDic);
+                    //評測 Exam
+                    List<string> fixExamIdList = await FixDataService.FixExamInfo(client, schoolCode, dataDic);
+                    //學生 Student
+                    List<string> fixStudentIdList = await FixDataService.FixStudentInfo(client, schoolCode, dataDic);
+
+                    return Ok(new { schoolCode, fixClassIdList, fixCourseIdList, fixKnowledgeIdList, fixItemIdList, fixPaperIdList, fixVolumeIdList, fixExamIdList, fixStudentIdList });
+                }
+                else
+                {
+                    string message = "No period is changed";
+                    return Ok(new { message });
+                }
+            }
+            catch (Exception ex)
+            {
+                //await _dingDing.SendBotMsg($"TEAMModelFunction,ActivityHttpTrigger,fix-periodid()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+        }
     }
 }

+ 157 - 0
TEAMModelOS/Services/Common/BlobService.cs

@@ -0,0 +1,157 @@
+using Azure;
+using Azure.Cosmos;
+using Azure.Storage.Blobs;
+using HTEXLib.COMM.Helpers;
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+
+namespace TEAMModelOS.Services.Common
+{
+    public static class BlobService
+    {
+        public static async Task<UsedBlob> GetBlobUsed(CosmosClient clientc, BlobContainerClient clients, IDatabase clientr, string scope, string containerName)
+        {
+            UsedBlob result = new UsedBlob();
+            try
+            {
+                //学校已经分配给所有教师的空间大小GB。
+                long teach = 0;
+                if (scope.Equals("school"))
+                {
+                    await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT sum(c.size) as size FROM c ",
+                        requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{containerName}") }))
+                    {
+                        var json = await JsonDocument.ParseAsync(item.ContentStream);
+                        foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            if (elmt.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
+                            {
+                                teach = _size.GetInt32();
+                                break;
+                            }
+                        }
+                    }
+                }
+                //使用blob狀況
+                bool getBlobCatalogFromBlob = false; //是否直接去blob取得使用狀況 false:否(Redis)  true:是(Blob)
+                long blobsize = 0;
+                RedisValue value = default;
+                value = clientr.HashGet($"Blob:Record", containerName);
+                if (value != default && !value.IsNullOrEmpty)
+                {
+                    JsonElement record = value.ToString().ToObject<JsonElement>();
+                    if (record.TryGetInt64(out blobsize))
+                    {
+                    }
+                    else
+                    {
+                        getBlobCatalogFromBlob = true;
+                    }
+                }
+                else
+                {
+                    getBlobCatalogFromBlob = true;
+                }
+                Dictionary<string, double?> catalog = new Dictionary<string, double?>();
+                SortedSetEntry[] Scores = clientr.SortedSetRangeByScoreWithScores($"Blob:Catalog:{containerName}");
+                if (Scores != null)
+                {
+                    foreach (var score in Scores)
+                    {
+                        double val = score.Score;
+                        string key = score.Element.ToString();
+                        catalog.Add(key, val);
+                    }
+                }
+                if (!getBlobCatalogFromBlob)
+                {
+                    result.size = blobsize;
+                    result.catalog = catalog;
+                    result.teach = teach;
+                    return result;
+                }
+                else
+                {
+                    var size = await clients.GetBlobsCatalogSize();
+                    result.size = size.Item1;
+                    result.catalog = size.Item2;
+                    result.teach = teach;
+                    return result;
+                }
+            }
+            catch (Exception ex)
+            {
+                //await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},Channel/Create()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return result;
+            }
+        }
+
+        /// <param name="scope">school or private</param>
+        /// <param name="containerName">school code or tmid</param>
+        /// <param name="type">doc, res, item... Empty if no need.</param>
+        /// <param name="periodId">Only for school</param>
+        public static async Task<List<BlobCount>> BloblogCount(CosmosClient clientc, string scope, string containerName, string type, string periodId)
+        {
+            List<BlobCount> bloblogcnt = new List<BlobCount>();
+            try
+            {
+                //必須項檢查
+                if(scope.Equals("school") && string.IsNullOrWhiteSpace(periodId))
+                {
+                    return bloblogcnt;
+                }
+                //資料取得
+                var queryslt = new StringBuilder();
+                queryslt.Append($"SELECT COUNT(1) AS count, c.type FROM c ");
+                if (scope.Equals("school"))
+                {
+                    queryslt.Append($"JOIN A1 IN c.periodId WHERE A1 IN ('{periodId}') ");
+                    if(!string.IsNullOrWhiteSpace(type)) queryslt.Append($"AND c.type='{type}'");
+                    queryslt.Append("GROUP BY c.type");
+                   await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<BlobCount>(queryText: queryslt.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Bloblog-{containerName}") }))
+                    {
+                        bloblogcnt.Add(item);
+                    }
+                }
+                else if (scope.Equals("private"))
+                {
+                    if (!string.IsNullOrWhiteSpace(type)) queryslt.Append($"AND c.type='{type}'");
+                    queryslt.Append("GROUP BY c.type");
+                    await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<BlobCount>(queryText: queryslt.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Bloblog-{containerName}") }))
+                    {
+                        bloblogcnt.Add(item);
+                    }
+                }
+
+                return bloblogcnt;
+            }
+            catch (Exception ex)
+            {
+                return bloblogcnt;
+            }
+        }
+
+        public class UsedBlob
+        {
+            public long teach { get; set; }
+            public long? size { get; set; }
+            public Dictionary<string, double?> catalog { get; set; }
+
+        }
+
+        public class BlobCount
+        {
+            public string type { get; set; }
+            public int count { get; set; }
+        }
+    }
+}