Просмотр исходного кода

Merge branch 'develop' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop

zj 2 лет назад
Родитель
Сommit
2f5dff5122

+ 1 - 1
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -2219,7 +2219,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
         {
             try
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Imei AF call\n{msg.ToJsonString()}", GroupNames.成都开发測試群組);
+                //await _dingDing.SendBotMsg($"IES5,{Environment.GetEnvironmentVariable("Option:Location")},Imei AF call\n{msg.ToJsonString()}", GroupNames.成都开发測試群組);
                 using var json = JsonDocument.Parse(msg);
                 if (json.RootElement.TryGetProperty("channel", out JsonElement channel) &&
                     json.RootElement.TryGetProperty("userid", out JsonElement userid) &&

+ 60 - 1
TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs

@@ -68,6 +68,65 @@ namespace TEAMModelOS.SDK
             //_dingDing = dingDing;
 
         }
+
+        public class CoreAPIToken { 
+            public string id_token { get; set; }
+            public string access_token { get; set; }
+            public string expires_in { get; set; }
+            public string token_type { get; set; }
+        }
+
+        public async Task<(HttpStatusCode code , CoreAPIToken token )> GetCoreAPIoAuth2Token(Dictionary<string,object> data, string location, IConfiguration _configuration, DI.DingDing _dingDing) {
+            try
+            {
+                var url = _configuration.GetValue<string>("HaBookAuth:CoreAPI");
+                //url = "https://api2-rc.teammodel.cn";
+                url = $"{url}/service/sandsms/pin";
+                var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                if (location.Contains("China"))
+                {
+                    location = "China";
+                }
+                else if (location.Contains("Global"))
+                {
+                    location = "Global";
+                }
+                var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location);
+                if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
+                {
+                    _httpClient.DefaultRequestHeaders.Remove("Authorization");
+                }
+                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+                HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync(url, data);
+                if (responseMessage.Content != null)
+                {
+                    string content = await responseMessage.Content.ReadAsStringAsync();
+                    if (!string.IsNullOrWhiteSpace(content))
+                    {
+                        CoreAPIToken coreAPI = content.ToObject<CoreAPIToken>();
+                        return (responseMessage.StatusCode, coreAPI);
+                    }
+                    else
+                    {
+                        return (responseMessage.StatusCode, null);
+                    }
+                }
+                else
+                {
+                    return (responseMessage.StatusCode, null);
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{location}验证码发送异常:\n{ex.Message}\n{ex.StackTrace}", DI.GroupNames.醍摩豆服務運維群組);
+                return (HttpStatusCode.InternalServerError, null);
+            }
+
+        }
+
         /*
         hubName	string	Optional	指定要傳送到哪個訊息中樞,若沒給則不傳送端外通知,只會發送端內通知。(目前只有"hita"及"hita5"能使用)
         sender	string	Optional	發送訊息的來源端
@@ -204,7 +263,7 @@ namespace TEAMModelOS.SDK
                                 {
                                     urlAction = "https://test.teammodel.cn/core/process-notify";
                                 }
-                                byte[] byts = Encoding.Unicode.GetBytes(replaceData.ToJsonString());
+                                byte[] byts = Encoding.Default.GetBytes(replaceData.ToJsonString());
                                 var  rdata=Convert.ToBase64String(byts);
                                 urlAction = $"{urlAction}?notifyCode={notifyCode}&data={rdata}";
                                 if (msgs.Count == 3)

+ 5 - 1
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -5060,7 +5060,11 @@ const LANG_EN_US = {
             class: "Course",
             point: "Point",
             semesters: "Semester Filter",
-        }
+        },
+        art: {
+            relatedFile: "Upload the relevant files",
+            noExam: "No reviews are published under the current subject, and no answers are required."
+        },
     },
     // 问卷调查
     survey: {

+ 4 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -5065,6 +5065,10 @@ const LANG_ZH_CN = {
             point: "记分",
             semesters: "学期筛选",
         },
+        art: {
+            relatedFile: "上传相关文件",
+            noExam: "当前科目下未发布评测,无需作答。"
+        },
     },
     // 问卷调查
     survey: {

+ 4 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -5065,6 +5065,10 @@ const LANG_ZH_TW = {
             point: "記分",
             semesters: "學期篩選",
         },
+        art: {
+            relatedFile: "上傳相關文件",
+            noExam: "當前科目下未發佈評測,無需作答。"
+        },
     },
     // 问卷调查
     survey: {

+ 16 - 24
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/ArtView.vue

@@ -6,38 +6,29 @@
                     <h2 class="title-rect-name">{{ hwInfo.quotaName }}</h2>
                 </div>
                 <div>
-                    <p class="head-box">作业描述:{{ hwInfo.workDesc }}</p>
-                    <p class="head-box">结束时间:{{ hwInfo.workEnd }}</p>
+                    <p class="head-box">{{ $t("homework.form.description") }}:{{ hwInfo.workDesc }}</p>
+                    <p class="head-box">{{ $t("learnActivity.createEv.endTime") }}:{{ hwInfo.workEnd }}</p>
                 </div>
                 <div style="margin-bottom: 20px" v-if="hwInfo.answer && hwInfo.answer.attachments.length">
                     <p>{{ $t('jyzx.offline.submitFile') }}:
                         <!-- <span style="float: right; margin-right: 10px; color: #4e77b1; cursor: pointer;" @click="deleteFile">{{ $t("cusMgt.delBatch") }}</span> -->
                     </p>
-                        <div class="file-show">
-                            <div v-for="(item, index) in hwInfo.answer.attachments" :key="index" class="one-show">
-                                <div class="repair-link-wrap-item-box">
-                                    <div class="file-icon">
-                                        <img :src="$tools.getFileThum(item.type, item.name)"/>
-                                    </div>
-                                    <div class="file-info">
-                                        <p class="file-name">{{ item.name }}</p>
-                                        <div>
-                                            <span @click="onPreview(item)" v-if="item.type !== 'other'">{{ $t('ability.review.preview')}}</span>
-                                            <span @click="onDownload(item)" v-if="item.type !== 'link'">{{ $t('ability.review.download')}}</span>
-                                        </div>
+                    <div class="file-show">
+                        <div v-for="(item, index) in hwInfo.answer.attachments" :key="index" class="one-show">
+                            <div class="repair-link-wrap-item-box">
+                                <div class="file-icon">
+                                    <img :src="$tools.getFileThum(item.type, item.name)"/>
+                                </div>
+                                <div class="file-info">
+                                    <p class="file-name">{{ item.name }}</p>
+                                    <div>
+                                        <span @click="onPreview(item)" v-if="item.type !== 'other'">{{ $t('ability.review.preview')}}</span>
+                                        <span @click="onDownload(item)" v-if="item.type !== 'link'">{{ $t('ability.review.download')}}</span>
                                     </div>
                                 </div>
                             </div>
                         </div>
-                        <!-- <Table :ref="{'selection': true}" :columns="fileCol" :data="hwInfo.answer.attachments" @on-selection-change="selectionChange">
-                            <template slot-scope="{row}" slot="name">
-                                <span v-show="row.prime" class="school-gade" style="display: inline-block; background: #a2b02e;">{{ $t("jyzx.offline.mainFile") }}</span>
-                                {{ row.name }}
-                            </template>
-                            <template slot-scope="{row}" slot="action">
-                                <Icon type="md-download" size="20" @click="loadAttach(row)" :title="$t('jyzx.offline.upload')" />
-                            </template> 
-                        </Table> -->
+                    </div>
                 </div>
                 <Upload
                     multiple
@@ -51,7 +42,7 @@
                         <Icon type="ios-cloud-upload" size="52" :style="{color: hwInfo.answer ? '#b4b4b4' : '#2d8cf0' }"></Icon>
                         <p>
                             <span>
-                                {{ hwInfo.answer ? $t('jyzx.offline.againLoad') : "上传相关文件"}}
+                                {{ hwInfo.answer ? $t('jyzx.offline.againLoad') : $t('studentWeb.art.relatedFile')}}
                             </span>
                         </p>
                     </div>
@@ -257,6 +248,7 @@ export default {
                         artId: this.getItemTitle.id,
                         quotaId: this.hwInfo.quotaId,
                         attachments: content,
+                        classId: this.getItemTitle.classIds[0], //行政班一个学生只会存在一个班级
                     }
                     // 如果是重新上传,要再传一个works里面的id
                     if(this.hwInfo.answer) params.id = this.hwInfo.answer.id

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

@@ -912,6 +912,8 @@
                     if(this.getItemTitle.type === "Atr") {
                         req.scode = `Exam-${codes}`
                         req.id = this.getExamInfo.id
+                        req.artId = this.getItemTitle.id
+                        req.quotaId = localStorage.getItem("quotaId")
                     }
                     this.$api.studentWeb.SaveStuExamPaper(req).then(res => {
                         if (res) {

+ 15 - 4
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperView.vue

@@ -90,8 +90,10 @@
                                 <LessonTestReport :paperInfo="selectData" :examInfo="chooseData" :chartsData="chartsData"
                                         :average="average[activeIndex]" :quData="quData" />
                             </div>
-                            <div v-if="(nowActive.type === 'Atr' && !examLists.length)">
-                                当前科目下未发布评测,无需作答。
+                            
+                            <div class="scoreboard" v-if="(nowActive.type === 'Atr' && !examLists.length)">
+                                <svg-icon icon-class="handonHint" class="warm-icon" />
+                                <span class="warm-hint">{{ $t("studentWeb.art.noExam") }}</span>
                             </div>
                         </div>
                     </TabPane>
@@ -104,7 +106,7 @@
                             <StudentScore :examData="paperData" :stuData="stuData"></StudentScore>
                         </div>
                     </TabPane> -->
-                    <TabPane label='上传资料' name="hw" v-if="hwLists.length">
+                    <TabPane :label="$t('ability.review.upload')" name="hw" v-if="hwLists.length">
                         <div v-for="(hw, index) in hwLists" :key="index">
                             <ArtView :hwInfo="hw" :hwIndex="index" @changeHwAnswer="changeHwAnswer" />
                         </div>
@@ -205,7 +207,10 @@
                                         examInfo = res.stus.find(stu => {
                                             return stu.id === task.acId
                                         })
-                                        if(examInfo) examInfo.isOrder = task.isOrder //0:默认,1:乱序
+                                        if(examInfo) {
+                                            examInfo.isOrder = task.isOrder //0:默认,1:乱序
+                                            examInfo.quotaId = item.id
+                                        }
                                     } else if(task.type === 2) {
                                         files = res.works.find(work => {
                                             return work.acId === task.acId
@@ -468,6 +473,7 @@
                         this.hwLists = item.homework
                         if(this.examLists.length) {
                             this.showPaper = 0
+                            localStorage.setItem("quotaId", item.exam[0].quotaId)
                             this.nowPaperId = item.exam[0] ? item.exam[0].id : null
                             /* this.$store.commit("ChangeItemName", item.exam[0]);
                             localStorage.setItem("Item", encodeURIComponent(JSON.stringify(item.exam[0]))) */
@@ -550,6 +556,7 @@
             examIdChange(item, index) {
                 this.showPaper = index
                 this.nowPaperId = item.id
+                localStorage.setItem("quotaId", item.quotaId)
                 this.getPaperData(true)
             },
             //时间格式化处理
@@ -613,6 +620,8 @@
                     this.artInfo = undefined
                     this.artExam = []
                     this.activeIndex = null
+                    this.selectData = undefined
+                    this.chooseData = undefined
                     // 艺术评测需要先获取详细信息
                     if(n.type === 'Atr') {
                         this.getArtInfo()
@@ -630,6 +639,8 @@
                         this.artInfo = undefined
                         this.artExam = []
                         this.activeIndex = null
+                        this.selectData = undefined
+                        this.chooseData = undefined
                         // 艺术评测需要先获取详细信息
                         if(this.getItemTitle.type === 'Atr') {
                             this.getArtInfo()

+ 158 - 8
TEAMModelOS/Controllers/System/CoreController.cs

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
+using Microsoft.OData.UriParser;
 using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
@@ -15,6 +16,7 @@ using System.Net;
 using System.Net.Http;
 using System.Reflection;
 using System.Runtime.InteropServices;
+using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
 using System.Text.RegularExpressions;
@@ -27,6 +29,8 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Service;
 using TEAMModelOS.SDK.PngQuant;
+using Top.Api;
+using static TEAMModelOS.SDK.CoreAPIHttpService;
 
 namespace TEAMModelOS.Controllers
 {
@@ -34,6 +38,7 @@ namespace TEAMModelOS.Controllers
     [ApiController]
     public class CoreController : ControllerBase
     {
+        private readonly AzureCosmosFactory _azureCosmos;
         private readonly AzureRedisFactory _azureRedis;
         private readonly AzureStorageFactory _azureStorage;
         private readonly DingDing _dingDing;
@@ -43,8 +48,9 @@ namespace TEAMModelOS.Controllers
         private readonly CoreAPIHttpService _coreAPIHttpService;
         private readonly IConfiguration _configuration;
         private readonly SnowflakeId _snowflakeId;
-        public CoreController(SnowflakeId snowflakeId,CoreAPIHttpService coreAPIHttpService,IConfiguration configuration,IPSearcher searcher, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, HttpClient httpClient)
+        public CoreController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId,CoreAPIHttpService coreAPIHttpService,IConfiguration configuration,IPSearcher searcher, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, HttpClient httpClient)
         {
+            _azureCosmos = azureCosmos;
             _searcher = searcher;
             _azureStorage = azureStorage;
             _dingDing = dingDing;
@@ -64,14 +70,158 @@ namespace TEAMModelOS.Controllers
         }
         [HttpGet("process-notify")]
         public async Task<IActionResult> ProcessNotify([FromQuery] NotifyData notifyData) {
-            await  _dingDing.SendBotMsg($"{notifyData.ToJsonString()}", GroupNames.成都开发測試群組);
-            switch (true) {
-                case bool when $"{notifyData.notifyCode}".Equals("request_school"):
-                    break;
-                case bool when $"{notifyData.notifyCode}".Equals("invite_school"):
-                    break;
+            string msg = "";
+            /// code =-1 参数异常,0 凭据失效 1不是管理员,2操作成功,3服务端异常
+            int code = -1;
+            try {
+                //await _dingDing.SendBotMsg($"{notifyData.ToJsonString()}", GroupNames.成都开发測試群組);
+                string opttmdid = "";
+                if (!string.IsNullOrWhiteSpace(notifyData.ticket))
+                {
+                    var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                    (HttpStatusCode statusCode, CoreAPIToken token) = await _coreAPIHttpService.GetCoreAPIoAuth2Token(
+                         new Dictionary<string, object> { { "client_id", clientID }, { "grant_type", "authorization_code" }, { "code", notifyData.ticket } }, _option.Location, _configuration, _dingDing);
+                    if (statusCode.Equals(HttpStatusCode.OK))
+                    {
+                        var jwt = new JwtSecurityToken(token.id_token);
+                        opttmdid = jwt.Payload.Sub;
+                    }
+                    else {
+                        code = 0;
+                        msg = "凭据失效";
+                    }
+                }
+                else {
+                    code = -1;
+                    msg = "凭据参数异常";
+                }
+                var dataByte = Convert.FromBase64String(notifyData.data);
+                string data = Encoding.Default.GetString(dataByte);
+                Dictionary<string, object> dict = data.ToObject<Dictionary<string, object>>();
+                dict.TryGetValue("schoolId", out object _schoolId);
+                dict.TryGetValue("tmdid", out object _tmdid);
+                if (!string.IsNullOrWhiteSpace($"{_schoolId}") && !string.IsNullOrWhiteSpace($"{_tmdid}") && string.IsNullOrWhiteSpace(opttmdid))
+                {
+                    switch (true)
+                    {
+                        case bool when $"{notifyData.notifyCode}".Equals("request_school"):
+                            {
+                                Azure.Response adminResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                                .ReadItemStreamAsync(opttmdid, new Azure.Cosmos.PartitionKey($"Teacher-{_schoolId}"));
+                                //是否是管理员
+                                if (adminResponse.Status == 200)
+                                {
+                                    var adminTeacher = JsonDocument.Parse(adminResponse.Content).RootElement.Deserialize<SchoolTeacher>();
+                                    if (adminTeacher.roles.Contains("admin"))
+                                    {
+                                        Azure.Response teacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                                        .ReadItemStreamAsync($"{_tmdid}", new Azure.Cosmos.PartitionKey("Base"));
+                                        Azure.Response schoolTeacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                                               .ReadItemStreamAsync($"{_tmdid}", new Azure.Cosmos.PartitionKey($"Teacher-{_schoolId}"));
+                                        if (teacherResponse.Status == 200 && schoolTeacherResponse.Status == 200)
+                                        {
+                                            var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
+                                            var schoolTeacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<SchoolTeacher>();
+                                            //同意
+                                            if (notifyData.notifyEvent == 1)
+                                            {
+                                                teacher.schools.ForEach(school =>
+                                                {
+                                                    if (school.schoolId.Equals($"{_schoolId}"))
+                                                    {
+                                                        school.status = "join";
+                                                    }
+                                                });
+                                                schoolTeacher.status = "join";
+                                                await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(schoolTeacher, schoolTeacher.id, new Azure.Cosmos.PartitionKey(schoolTeacher.code));
+                                            }
+                                            //拒绝
+                                            if (notifyData.notifyEvent == 2)
+                                            {
+                                                //只有在未正式加入成功才能拒绝成功,防止HiTA消息未清除,再次操作。
+                                                teacher.schools.RemoveAll(z => z.schoolId.Equals($"{_schoolId}") && (z.status.Equals("request") || z.status.Equals("invite")));
+                                            }
+                                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(teacher, teacher.id, new Azure.Cosmos.PartitionKey(teacher.code));
+                                            code = 2;
+                                            msg = "操作成功";
+                                        }
+                                        else
+                                        {
+                                            code = -1;
+                                            msg = "教师不存在";
+                                        }
+
+                                    }
+                                    else
+                                    {
+                                        code = 1;
+                                        msg = "不是管理员";
+                                    }
+                                }
+                                else
+                                {
+                                    code = -1;
+                                    msg = "管理员不存在";
+                                }
+                            }
+                            break;
+                        case bool when $"{notifyData.notifyCode}".Equals("invite_school"):
+                            {
+                                Azure.Response teacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                                   .ReadItemStreamAsync($"{opttmdid}", new Azure.Cosmos.PartitionKey("Base"));
+                                Azure.Response schoolTeacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                                       .ReadItemStreamAsync($"{opttmdid}", new Azure.Cosmos.PartitionKey($"Teacher-{_schoolId}"));
+                                if (teacherResponse.Status == 200 && schoolTeacherResponse.Status == 200)
+                                {
+                                    var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
+                                    var schoolTeacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<SchoolTeacher>();
+                                    //同意
+                                    if (notifyData.notifyEvent == 1)
+                                    {
+                                        teacher.schools.ForEach(school =>
+                                        {
+                                            if (school.schoolId.Equals($"{_schoolId}"))
+                                            {
+                                                school.status = "join";
+                                            }
+                                        });
+                                        schoolTeacher.status = "join";
+                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(schoolTeacher, schoolTeacher.id, new Azure.Cosmos.PartitionKey(schoolTeacher.code));
+                                    }
+                                    //拒绝
+                                    if (notifyData.notifyEvent == 2)
+                                    {
+                                        //只有在未正式加入成功才能拒绝成功,防止HiTA消息未清除,再次操作。
+                                        teacher.schools.RemoveAll(z => z.schoolId.Equals($"{_schoolId}") && (z.status.Equals("request") || z.status.Equals("invite")));
+                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).DeleteItemStreamAsync($"{opttmdid}", new Azure.Cosmos.PartitionKey($"Teacher-{_schoolId}"));
+                                    }
+                                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(teacher, teacher.id, new Azure.Cosmos.PartitionKey(teacher.code));
+                                    code = 2;
+                                    msg = "操作成功";
+                                }
+                                else
+                                {
+                                    code = -1;
+                                    msg = "教师不存在";
+                                }
+                            }
+                            break;
+                    }
+                }
+                else
+                {
+                    code = -1;
+                    msg = "参数异常";
+                }
+            } catch (Exception ex) {
+                await _dingDing.SendBotMsg($"{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                code = 3;
+                msg = "服务端异常";
             }
-            return Ok();
+            string HostName = HttpContext.GetHostName();
+            var rurl = $"https://{HostName}/feedback?code={code}&msg={msg}";
+            return Redirect(rurl);
+            //return Ok(new { code,msg});
         }
         [HttpPost("sendsms/pin")]
         public async Task<IActionResult> SendSmsPinCode(JsonElement request)