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

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

liqk 4 лет назад
Родитель
Сommit
d3cb5ad054

+ 3 - 3
TEAMModelOS/Filter/ApiTokenAttribute.cs

@@ -48,9 +48,9 @@ namespace TEAMModelOS.Filter
                     jti = jwt.Payload.Jti;
                     name = jwt.Claims.FirstOrDefault(claim => claim.Type == "name")?.Value;
                     //处理限流问题
-                    if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(school) || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(jti))
+                    if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(school) && !string.IsNullOrEmpty(name)&& !string.IsNullOrEmpty(jti))
                     {
-                        context.Result = new BadRequestResult();
+                        pass = true;
                     }
                     else { 
                         
@@ -64,7 +64,7 @@ namespace TEAMModelOS.Filter
                     context.HttpContext.Items.Add("School", school);
                 }
                 else
-                    context.Result = new BadRequestResult();
+                    context.Result = new UnauthorizedResult();
             }
 
             public void OnResourceExecuted(ResourceExecutedContext context)

+ 38 - 0
TEAMModelAPI/Startup.cs

@@ -10,7 +10,11 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
+using System.IdentityModel.Tokens.Jwt;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
 
 namespace TEAMModelAPI
 {
@@ -27,6 +31,37 @@ namespace TEAMModelAPI
         // This method gets called by the runtime. Use this method to add services to the container.
         public void ConfigureServices(IServiceCollection services)
         {
+           
+            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
+            services.AddAuthentication(options => options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme)
+                .AddJwtBearer(options => //AzureADJwtBearer
+                {
+                    //options.SaveToken = true; //驗證令牌由服務器生成才有效,不適用於服務重啟或分布式架構
+                    options.Authority = Configuration["Option:Authority"];
+                    options.Audience = Configuration["Option:Audience"];
+                    options.RequireHttpsMetadata = true;
+                    options.TokenValidationParameters = new TokenValidationParameters
+                    {
+                        RoleClaimType = "roles",
+                        ValidAudiences = new string[] { Configuration["Option:Audience"], $"api://{Configuration["Option:Audience"]}" }
+                    };
+                    options.Events = new JwtBearerEvents();
+                    //下列事件有需要紀錄則打開
+                    //options.Events.OnMessageReceived = async context => { await Task.FromResult(0); };
+                    //options.Events.OnForbidden = async context => { await Task.FromResult(0); };
+                    //options.Events.OnChallenge = async context => { await Task.FromResult(0); };
+                    //options.Events.OnAuthenticationFailed = async context => { await Task.FromResult(0); };
+                    options.Events.OnTokenValidated = async context =>
+                    {
+                        if (!context.Principal.Claims.Any(x => x.Type == "http://schemas.microsoft.com/identity/claims/scope") //ClaimConstants.Scope
+                        && !context.Principal.Claims.Any(y => y.Type == "roles")) //ClaimConstants.Roles //http://schemas.microsoft.com/ws/2008/06/identity/claims/role
+                        {
+                            //TODO 需處理額外授權非角色及範圍的訪問異常紀錄
+                            throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
+                        }
+                        await Task.FromResult(0);
+                    };
+                });
             //設定跨域請求
             services.AddCors(options =>
             {
@@ -50,6 +85,9 @@ namespace TEAMModelAPI
             services.AddHttpClient();
             services.AddHttpClient<DingDing>();
             services.AddAzureServiceBus(Configuration.GetValue<string>("Azure:ServiceBus:ConnectionString"));
+            //HttpContextAccessor,并用来访问HttpContext。(提供組件或非控制器服務存取HttpContext)
+            services.AddHttpContextAccessor();
+            services.Configure<Option>(options => Configuration.GetSection("Option").Bind(options));
         }
 
         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

+ 1 - 2
TEAMModelAPI/TEAMModelAPI.csproj

@@ -5,7 +5,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\TEAMModelOS\TEAMModelOS.csproj" />
+    <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
   </ItemGroup>
-
 </Project>

+ 13 - 1
TEAMModelAPI/appsettings.json

@@ -6,5 +6,17 @@
       "Microsoft.Hosting.Lifetime": "Information"
     }
   },
-  "AllowedHosts": "*"
+  "AllowedHosts": "*",
+  "Option": {
+    "Location": "China",
+    "LocationNum": "8",
+    "HostName": "localhost:5001",
+    "AllowedHosts": [ "localhost", "*.teammodel.cn", "*.teammodel.net", "*.habookaclass.biz", "test" ],
+    "Authority": "https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b/v2.0", //China:"https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b/v2.0", //Global:"https://login.microsoftonline.com/73a2bcc5-fe99-4566-aa8a-07e7bb287df1/v2.0"
+    "Audience": "72643704-b2e7-4b26-b881-bd5865e7a7a5", //China:"72643704-b2e7-4b26-b881-bd5865e7a7a5",Global:"8768b06f-c5c5-4b0c-abfb-d7ded354626d"
+    "Issuer": "www.teammodel.cn",
+    "JwtSecretKey": "fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=",
+    "Exp": 86400,
+    "IdTokenSalt": "8263692E2213497BB55E74792B7900B4"
+  }
 }

+ 1 - 1
TEAMModelOS.SDK/DI/AzureStorage/AzureStorageFactory.cs

@@ -156,7 +156,7 @@ namespace TEAMModelOS.SDK.DI
                 BlobContainerSasPermissions blobContainerSasPermissions = BlobContainerSasPermissions.Read;
                 if (isRead)
                 {
-                    blobContainerSasPermissions = BlobContainerSasPermissions.Read;
+                    blobContainerSasPermissions = BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List;
                 }
                 else
                 {

+ 2 - 2
TEAMModelOS.SDK/Extension/JwtAuthExtension.cs

@@ -81,11 +81,11 @@ namespace TEAMModelOS.SDK.Extension
                 var handler = new JwtSecurityTokenHandler();
                 var validationParameters = new TokenValidationParameters
                 {
-                    RequireExpirationTime = true,
+                    RequireExpirationTime = false,
                     ValidateIssuer = false,
                     ValidateAudience = false,
                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
-                    //ValidateLifetime = false,
+                    ValidateLifetime = false,
                     //LifetimeValidator = LifetimeValidator,
                     ClockSkew = TimeSpan.Zero
                 };

+ 64 - 1
TEAMModelOS.SDK/Models/Cosmos/Api/OpenApi.cs

@@ -28,22 +28,85 @@ namespace TEAMModelOS.SDK.Models
 
             PartitionKey = "IES5-API";
         } 
+        /// <summary>
+        /// 接口名称
+        /// </summary>
         public string name { get; set; }
+        /// <summary>
+        /// 接口url
+        /// </summary>
         public string url { get; set; }
+        /// <summary>
+        /// 请求方法
+        /// </summary>
         public string method { get; set; }
+        /// <summary>
+        /// 开放的api接口
+        /// </summary>
         public string descr { get; set; }
         public int auth { get; set; }
-
     }
+    [TableName(Name = "OpenApi")]
+    public class Webhook : TableEntity
+    {
+        public Webhook()
+        {
 
+            PartitionKey = "IES5-WEBHOOK";
+        }
+        /// <summary>
+        /// 接口名称
+        /// </summary>
+        public string name { get; set; }
+        /// <summary>
+        /// 接口url
+        /// </summary>
+        public string url { get; set; }
+        /// <summary>
+        /// 请求方法
+        /// </summary>
+        public string method { get; set; }
+        /// <summary>
+        /// 开放的api接口
+        /// </summary>
+        public string descr { get; set; }
+        public int auth { get; set; }
+    }
     public class OpenApp : CosmosEntity {
+        /// <summary>
+        /// 图标
+        /// </summary>
         public string icon { get; set; }
+        /// <summary>
+        /// 应用名称
+        /// </summary>
         public string name { get; set; }
+        /// <summary>
+        /// 应用描述
+        /// </summary>
         public string descr { get; set; }
+        /// <summary>
+        /// 授权信息
+        /// </summary>
         public List<int> auths { get; set; } = new List<int>();
+        public List<int> webhooks { get; set; } = new List<int>();
+        /// <summary>
+        /// 学校编码
+        /// </summary>
         public string school { get; set; }
+        /// <summary>
+        /// 生成的token
+        /// </summary>
         public string token { get; set; }
         /// <summary>
+        /// domain的域名
+        /// </summary>
+        public string domain { get; set; }
+        /// <summary>
+        ///webhook
+        /// </summary>
+        public string webhookToken { get; set; }
+        /// <summary>
         /// 0禁用,1正常,2 token封禁
         /// </summary>
         public int status { get; set; }

+ 5 - 3
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.vue

@@ -1,5 +1,6 @@
 <template>
-    <div class="event-content">
+    <!-- <div class="event-content"> -->
+    <div class="eventContentArea-view">
         <!--<VoteHint v-if="this.$store.getters.getisVoteResulthover==true"/>-->
         <div v-if="getIsSelectedNow">
 
@@ -34,7 +35,7 @@
     import PaperView from "./EventContentTypeTemplate/PaperView";
     import VoteHint from "./EventContentTypeTemplate/VoteHint";
     import QuesNaire from "./EventContentTypeTemplate/QuesNaire";
-import { mapGetters } from 'vuex';
+    import { mapGetters } from 'vuex';
     export default {
         name: "EventContent",
         components: {
@@ -62,5 +63,6 @@ import { mapGetters } from 'vuex';
     };
 </script>
 
-<style>
+<style scoped>
+@import "EventContentArea.css";
 </style>

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

@@ -163,12 +163,43 @@
                             }
                             this.opentestWithSubject(this.paperData[0])
                         } else {
-                            this.$Message.warning('试卷已删除!')
+                            // this.$Message.warning('试卷已删除!')
+                            this.$Modal.confirm({
+                                title: "评测已被删除,是否删除此条记录?",
+                                okText: "删除",
+                                cancelText: "取消",
+                                onOk: () => {
+                                    this.delActivity()
+                                }
+                            })
                         }
                     })
                 }
                 this.isLoad = false
             },
+            // 删除不存在的问卷
+            delActivity() {
+                let roles = ""
+                if(this.$store.state.userInfo.roles.indexOf('student') > -1) {
+                    roles = 'student'
+                } else {
+                    roles = 'taecher'
+                }
+                let param = {
+                    'id': this.getItemTitle.id,
+                    'code': this.getItemTitle.code,
+                    "role": roles
+                }
+                this.$api.studentWeb.delActivity(param).then(res => {
+                    if(res) {
+						if(res.status == 200) {
+							this.$Message.success('评测已删除!')
+							this.$EventBus.$emit('delNotFound', this.getItemTitle.id)
+                            this.$store.commit("ChangeItemName", {})
+						}
+					}
+                })
+            },
             opentestWithSubject(item) {
                 if (item !== undefined) {
                     this.getPaper(item)

+ 20 - 4
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/QuesNaire.vue

@@ -242,7 +242,15 @@ import { mapGetters } from 'vuex';
 						this.$api.studentWeb.getSurveyInfo(params).then(async res => {
 							if (res) {
 								if(res.status == 404) {
-									this.delActivity()
+									this.isLoad = false
+									this.$Modal.confirm({
+										title: "问卷已被删除,是否删除此条记录?",
+										okText: "删除",
+										cancelText: "取消",
+										onOk: () => {
+											this.delActivity()
+										}
+									})
 								} else if(res.status == 200) {
 									this.surveyInfo = res.survey
 									this.surveyInfo.items = await this.getBlobItems(res.survey)
@@ -263,15 +271,23 @@ import { mapGetters } from 'vuex';
 			},
 			// 删除不存在的问卷
             delActivity() {
+                let roles = ""
+                if(this.$store.state.userInfo.roles.indexOf('student') > -1) {
+                    roles = 'student'
+                } else {
+                    roles = 'taecher'
+                }
                 let param = {
                     'id': this.getItemTitle.id,
                     'code': this.getItemTitle.code,
-                    "role": "teacher"
+                    "role": roles
                 }
                 this.$api.studentWeb.delActivity(param).then(res => {
                     if(res) {
-						if(res.status == 500) {
-							this.$Message.warning('问卷已删除!')
+						if(res.status == 200) {
+							this.$Message.success('问卷已删除!')
+							this.$EventBus.$emit('delNotFound', this.getItemTitle.id)
+							this.$store.commit("ChangeItemName", {})
 						}
 					}
                 }).finally(()=>{

+ 11 - 3
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Vote.vue

@@ -235,15 +235,23 @@
             },
             // 删除不存在的投票
             delActivity() {
+                let roles = ""
+                if(this.$store.state.userInfo.roles.indexOf('student') > -1) {
+                    roles = 'student'
+                } else {
+                    roles = 'taecher'
+                }
                 let param = {
                     'id': this.getItemTitle.id,
                     'code': this.getItemTitle.code,
-                    "role": "teacher"
+                    "role": roles
                 }
                 this.$api.studentWeb.delActivity(param).then(res => {
                     if(res) {
-                        if(res.status == 500) {
-                            this.$Message.warning('投票已删除!')
+                        if(res.status == 200) {
+                            this.$Message.success('投票已删除!')
+                            this.$EventBus.$emit('delNotFound', this.getItemTitle.id)
+                            this.$store.commit("ChangeItemName", {})
                         }
                     }
                 }).finally(()=>{

+ 18 - 1
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue

@@ -216,7 +216,12 @@ import { mapGetters, mapState } from 'vuex';
     import PreviewProgressPie from "../EventView/PreviewProgressPie";
     export default {
         name: "EventList",
-        mounted() { 
+        mounted() {
+            this.$EventBus.$off('delNotFound')
+            this.$EventBus.$on('delNotFound', id => {
+                console.log(id);
+                this.delListData(id)
+            })
             this.getActivityInfo()
             this.selectedCondition(this.getItemTitle);
             if (this.getIsSelectedNow == false) {
@@ -524,6 +529,18 @@ import { mapGetters, mapState } from 'vuex';
                     return true;
                 } else return false;
             },
+            // 删除页面上不不存在的数据
+            delListData(id) {
+                if(this.eventList) {
+                    for(let i = 0; i < this.eventList.length; i++) {
+                        if(this.eventList[i].id == id) {
+                            this.eventList.splice(i, 1)
+                            break
+                        }
+                    }
+                    this.predealMockdatafirstItem()
+                }
+            },
 
             /* =======未调用======== */
             //取得目前滑鼠座標

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

@@ -1,7 +1,8 @@
 <template>
     <div class="event-view">
         <EventList :class="{ 'hide-sidebars': getSidebarisOpen == false}"></EventList>
-        <EventContentArea :class="{'eventContentArea-Span': getSidebarisOpen == false}"></EventContentArea>
+        <!-- <EventContentArea :class="{'eventContentArea-Span': getSidebarisOpen == false}"></EventContentArea> -->
+        <EventContent :class="{'eventContentArea-Span': getSidebarisOpen == false}"></EventContent>
         <!--<CommentList v-if=" $store.getters.getOpenCommentList == true && getSidebarisOpen == false" />-->
     </div>
 </template>
@@ -10,6 +11,7 @@
     import CommentList from "./CommentList.vue";
     import EventList from "./EventList";
     import EventContentArea from "./EventContentArea.vue";
+    import EventContent from "./EventContent.vue";
     import { mapGetters } from 'vuex';
     export default {
         name: "EventView",
@@ -17,6 +19,7 @@
             EventList,
             EventContentArea,
             CommentList,
+            EventContent,
         },
         data() {
             return {

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.vue

@@ -299,7 +299,7 @@ export default {
 
     created() {
         (this.MyName = this.$t("studentWeb.homeView-title")),
-            this.$emit("onNavNo", this.MyNo)
+        this.$emit("onNavNo", this.MyNo)
         this.$emit("onNavName", this.MyName)
     },
     mounted() {

+ 68 - 0
TEAMModelOS/Controllers/Common/CommonController.cs

@@ -44,6 +44,74 @@ namespace TEAMModelOS.Controllers.Common
             _clientFactory = clientFactory;
         }
 
+        //立即结束某个活动
+        [ProducesDefaultResponseType]
+       // [AuthToken(Roles = "teacher,admin")]
+        [HttpPost("count-activity")]
+        public async Task<IActionResult> countActivity(JsonElement element)
+        {
+            try
+            {
+                List<string> keys = new List<string> { "Vote","Exam", "Survey","Homework","Learn" };
+                var client = _azureCosmos.GetCosmosClient();
+                element.TryGetProperty("code", out JsonElement code) ;
+                element.TryGetProperty("tmdid", out JsonElement tmdid);
+                Dictionary<string, int> countall = new Dictionary<string, int>();
+                foreach (var key in keys) {
+                    string queryList = $"select  count(1) as countschool  from c where c.pk='{key}' ";
+                    if (code.ValueKind.Equals(JsonValueKind.String)) {
+                        var queryschool = $"select  count(1) as countschool  from c where c.pk='{key}' ";
+                        await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(
+                           queryText: queryschool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{key}-{code}") }))
+                        {
+                            using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                            if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                            {
+                                foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                {
+                                    if (countall.ContainsKey(key.ToLower()))
+                                    {
+                                        countall[key.ToLower()] = countall[key.ToLower()] + obj.GetProperty("countprivate").GetInt32();
+                                    }
+                                    else
+                                    {
+                                        countall.Add(key.ToLower(), obj.GetProperty("countschool").GetInt32());
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    if (tmdid.ValueKind.Equals(JsonValueKind.String)) {
+                        var queryprivate = $"select  count(1) as countprivate  from c where c.pk='{key}' ";
+                        await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(
+                           queryText: queryprivate, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{key}-{tmdid}") }))
+                        {
+                            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())
+                                {
+                                    if (countall.ContainsKey(key.ToLower()))
+                                    {
+                                        countall[key.ToLower()] = countall[key.ToLower()] + obj.GetProperty("countprivate").GetInt32();
+                                    }
+                                    else
+                                    {
+                                        countall.Add(key.ToLower(), obj.GetProperty("countprivate").GetInt32());
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                return Ok(new { countall });
+            } catch (Exception ex) {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/count-activity\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(); 
+            }
+        }
+
         //立即结束某个活动
         [ProducesDefaultResponseType]
         [AuthToken(Roles = "teacher,admin")]

+ 20 - 3
TEAMModelOS/Controllers/Core/OpenApiController.cs

@@ -72,17 +72,34 @@ namespace TEAMModelOS.Controllers.Core
          {}
          */
         /// <summary>
-        /// 获取
+        /// 获取开放api接口
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
       //  [AuthToken(Roles = "admin")]
-        [HttpPost("get")]
-        public async Task<IActionResult> Get(JsonElement json) {
+        [HttpPost("get-api")]
+        public async Task<IActionResult> GetApi(JsonElement json) {
             List<OpenApi> apis = await _azureStorage.FindListByDict<OpenApi>(new Dictionary<string, object>() { { "PartitionKey", "IES5-API" } });
             return Ok(new { apis });
         }
+        /*
+         数据实例
+         {}
+         */
+        /// <summary>
+        /// 获取webhook 通知接口
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //  [AuthToken(Roles = "admin")]
+        [HttpPost("get-webhook")]
+        public async Task<IActionResult> GetWebhook(JsonElement json)
+        {
+            List<Webhook> webhooks = await _azureStorage.FindListByDict<Webhook>(new Dictionary<string, object>() { { "PartitionKey", "IES5-WEBHOOK" } });
+            return Ok(new { webhooks });
+        }
         /*
         {
            "id": "4a54e95e-ef13-46ec-91ac-e55919d09f9e", //新增没有id  ,更新带id  

+ 49 - 32
TEAMModelOS/Controllers/Syllabus/ShareController.cs

@@ -27,7 +27,7 @@ namespace TEAMModelOS.Controllers
     //[Authorize(Roles = "IES5")]
     [Route("teacher/share")]
     [ApiController]
-    public class ShareController: ControllerBase
+    public class ShareController : ControllerBase
     {
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly SnowflakeId _snowflakeId;
@@ -40,7 +40,7 @@ namespace TEAMModelOS.Controllers
             _azureCosmos = azureCosmos;
             _snowflakeId = snowflakeId;
             _dingDing = dingDing;
-            _option = option?.Value; 
+            _option = option?.Value;
             _configuration = configuration;
             _notificationService = notificationService;
         }
@@ -67,7 +67,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("to")]
-       // [AuthToken(Roles = "Teacher")]
+        // [AuthToken(Roles = "Teacher")]
         public async Task<IActionResult> To(ShareData request) {
             // var (id, _, _, _) = HttpContext.GetAuthTokenInfo();
 
@@ -82,11 +82,11 @@ namespace TEAMModelOS.Controllers
                         if (syllabusD.auth.IsNotEmpty())
                         {
                             List<SyllabusAuth> syllabusAuths = new List<SyllabusAuth>();
-                            syllabusD.auth.ForEach(x=> {
-                                if (request.tmdInfo.Select(tmd=>tmd.tmdid).Contains(x.tmdid)) {
+                            syllabusD.auth.ForEach(x => {
+                                if (request.tmdInfo.Select(tmd => tmd.tmdid).Contains(x.tmdid)) {
                                     syllabusAuths.Add(x);
-                                }   
-                            }) ;
+                                }
+                            });
                             syllabusAuths.ForEach(x => {
                                 syllabusD.auth.Remove(x);
                             });
@@ -94,13 +94,13 @@ namespace TEAMModelOS.Controllers
                             request.tmdInfo.ForEach(async x => {
                                 await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync<Share>(request.syllabusId, new PartitionKey($"Share-{x.tmdid}"));
                             });
-                            
+
                         }
                     }
                     else if (request.opt.Equals("add") || request.opt.Equals("edit"))
                     {
                         (Syllabus syllabus, List<Share> shares) = DoAuth(request, syllabusD);
-                        shares.ForEach(async x=> {
+                        shares.ForEach(async x => {
                             await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Share>(x, new PartitionKey($"{x.code}"));
                             //发送共编或分享通知
                             Notification notification = new Notification
@@ -108,9 +108,9 @@ namespace TEAMModelOS.Controllers
                                 hubName = "hita",
                                 type = "msg",
                                 from = $"ies5:{request.school}",
-                                to = new List<string>() { x.code.Replace("Share-","") },
+                                to = new List<string>() { x.code.Replace("Share-", "") },
                                 label = $"{x.type}_syllabus",
-                                body = new { biz = x.type, tmdid = x.issuer,  schoolcode = $"{request.school}",  status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
+                                body = new { biz = x.type, tmdid = x.issuer, schoolcode = $"{request.school}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
                                 expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
                             };
                             var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
@@ -121,7 +121,7 @@ namespace TEAMModelOS.Controllers
                         });
                         await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<Syllabus>(syllabus, new PartitionKey($"Syllabus-{request.volumeId}"));
 
-                        
+
                     }
                 }
                 else if (request.scope.Equals("private"))
@@ -133,7 +133,7 @@ namespace TEAMModelOS.Controllers
                         {
                             List<SyllabusAuth> syllabusAuths = new List<SyllabusAuth>();
                             syllabusD.auth.ForEach(x => {
-                                if (request.tmdInfo.Select(tmd=>x.tmdid).Contains(x.tmdid))
+                                if (request.tmdInfo.Select(tmd => x.tmdid).Contains(x.tmdid))
                                 {
                                     syllabusAuths.Add(x);
                                 }
@@ -156,9 +156,9 @@ namespace TEAMModelOS.Controllers
                         await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabusD, new PartitionKey($"Syllabus-{request.volumeId}"));
                     }
                 }
-                return Ok(new { code=200});
+                return Ok(new { code = 200 });
             }
-            catch (Exception ex ) {
+            catch (Exception ex) {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/to\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
             return Ok(new { code = 500 });
@@ -182,12 +182,12 @@ namespace TEAMModelOS.Controllers
                     createTime = now,
                     school = request.school,
                     scope = request.scope,
-                    type =request.type,
+                    type = request.type,
                     agree = request.agree
                 };
                 shares.Add(share);
             });
-            
+
             if (syllabus.auth.IsNotEmpty())
             {
                 request.tmdInfo.ForEach(xmd => {
@@ -196,7 +196,7 @@ namespace TEAMModelOS.Controllers
                     int indx = 0;
                     for (int index = 0; index < syllabus.auth.Count; index++)
                     {
-                        if (syllabus.auth[index].tmdid == xmd.tmdid && request.type== syllabus.auth[index].type)
+                        if (syllabus.auth[index].tmdid == xmd.tmdid && request.type == syllabus.auth[index].type)
                         {
                             flag = true;
                             indx = index;
@@ -273,7 +273,7 @@ namespace TEAMModelOS.Controllers
                 {
                     syllabus.auth.ForEach(x =>
                     {
-                        if (x.tmdid == $"{code}" && x.type==$"{type}")
+                        if (x.tmdid == $"{code}" && x.type == $"{type}")
                         {
                             if ($"{opt}".Equals("ignore"))
                             {
@@ -282,34 +282,51 @@ namespace TEAMModelOS.Controllers
                             else if ($"{opt}".Equals("ignore")) {
                                 x.agree = 1;
                             }
-                          
+
                         }
                     });
                 }
-                if (share.scope == "school")
-                {
-                    syllabus = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
-                }
-                else if (share.scope == "private")
-                {
-                    syllabus = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
-                }
+
                 if ($"{opt}".Equals("ignore"))
                 {
                     share.agree = 1;
-                    await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Share>(share,$"{id}", new PartitionKey($"Share-{type}-{code}"));
+                    await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Share>(share, $"{id}", new PartitionKey($"Share-{type}-{code}"));
                 }
                 else if ($"{opt}".Equals("ignore"))
                 {
                     await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync<Share>($"{id}", new PartitionKey($"Share-{type}-{code}"));
                 }
+                if (share.scope == "school")
+                {
+                    syllabus = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
+                }
+                else if (share.scope == "private")
+                {
+                    syllabus = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
+                }
                 return Ok(new { status = 200 });
             } catch (Exception ex) {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/agree-share()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return Ok(new { status = 500 });
             }
         }
-
+        [ProducesDefaultResponseType]
+        [HttpPost("del-share")]
+        public async Task<IActionResult> DelShare(JsonElement request) {
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                if (!request.TryGetProperty("type", out JsonElement type)) { return BadRequest(); }
+                if (!request.TryGetProperty("code", out JsonElement code)) { return BadRequest(); }
+                if (!request.TryGetProperty("id", out JsonElement id)) { return BadRequest(); }
+                await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync<Share>($"{id}", new PartitionKey($"Share-{type}-{code}"));
+                return Ok(new { status = 200 });
+            } catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/del-share()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return Ok(new { status = 500 });
+            }
+        }
         /// <summary>
         /// {"code":"教师编码","type":"coedit/share","id":"册别id"} 
         /// 教师拉取自己收到的分享及共编
@@ -339,12 +356,12 @@ namespace TEAMModelOS.Controllers
                     shares.Add(item);
                 }
                 var sharesGp  =  shares.GroupBy(x => new {id= x.volumeId ,code=x.scope=="school"?x.school:x.issuer}).Select(y=>new { id=y.Key.id,code=y.Key.code,list=y.ToList()});
-                return Ok(new { shares = sharesGp });
+                return Ok(new { shares = sharesGp ,status=200});
             }
             catch (Exception ex)
             {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/find()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
-                return BadRequest();
+                return Ok(new {  status = 404 });
             }
 
         }