Ver código fonte

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

OnePsycho 4 anos atrás
pai
commit
9151c85ad1

+ 1 - 1
TEAMModelFunction/TriggerExam.cs

@@ -222,7 +222,7 @@ namespace TEAMModelFunction
                                                             int month = DateTimeOffset.UtcNow.Month;
                                                             int day = DateTimeOffset.UtcNow.Day;
                                                             int time =  month > semester.month ?  0 :  1;
-                                                            int eyear = year - time;
+                                                            int eyear = classroom.year - time;
                                                             result.gradeId = (year - eyear).ToString();
                                                         }
                                                     }

+ 171 - 0
TEAMModelOS.SDK/Extension/CoreTokenExtensions.cs

@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.Identity.Client;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Azure.Security.KeyVault.Secrets;
+using Azure.Core;
+using Azure.Identity;
+using System.Net.Http;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+
+namespace TEAMModelOS.SDK.Extension
+{
+    public static class CoreTokenExtensions
+    {       //var issuer = Configuration.GetValue<string>("JwtSettings:Issuer");
+            //var signKey = Configuration.GetValue<string>("JwtSettings:SignKey");
+        private const string issuer = "account.teammodel";
+        //Azure AD 租用戶識別碼(國際、大陸)
+        private static List<string> tenantids = new List<string> { "73a2bcc5-fe99-4566-aa8a-07e7bb287df1", "4807e9cf-87b8-4174-aa5b-e76497d7392b" };
+        private static ConcurrentDictionary<string, KeyVaultSecret> KeyVaultSecrets { get; } = new ConcurrentDictionary<string, KeyVaultSecret>();
+              
+
+        #region  Access Token
+        /// <summary>
+        /// 產生AccessToken
+        /// </summary>
+        /// <param name="clientID"></param>
+        /// <param name="location">服務位置,Global or China ...</param>
+        /// <returns></returns>
+        public static async ValueTask<AuthenticationResult> CreateAccessToken(string clientID, string clientSecret, string location)
+        {
+            //從金鑰庫取出秘密,此作法讓所有端直接刷新金鑰,無需傳送秘密,SPA更適用
+            var secret = clientSecret ?? (await GetClientIDSecret(clientID, location)).Value;
+
+            var sts = Enum.Parse<STSEndpoint>(location, true);
+
+            IConfidentialClientApplication app;
+            app = ConfidentialClientApplicationBuilder.Create(clientID)
+                                                      .WithClientSecret(secret)
+                                                      .WithAuthority(new Uri(sts.GetDescriptionText()))
+                                                      .Build();
+            var scope = ((STSScope)sts).GetDescriptionText();
+            var result = await app.AcquireTokenForClient(new[] { scope }).ExecuteAsync();
+            return result;
+        }
+
+        /// <summary>
+        /// 驗證是否為公司Azure發行金鑰,支援大陸國際
+        /// </summary>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        public static bool ValidateAccessToken(JwtSecurityToken token)
+        {
+            try
+            {
+                if (token.Payload.TryGetValue("tid", out var value) && value is string tokenTenantId)
+                {
+                    return tenantids.Contains(tokenTenantId);
+                }
+                return false;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+        }
+
+        public static bool ValidateIdToken(string token, string salt)
+        {
+            try
+            {
+                var handler = new JwtSecurityTokenHandler();
+                var validationParameters = new TokenValidationParameters
+                {
+                    RequireExpirationTime = true,
+                    ValidateIssuer = false,
+                    ValidateAudience = false,
+                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
+                    ValidateLifetime = false,
+                    //LifetimeValidator = LifetimeValidator,
+                    ClockSkew = TimeSpan.Zero                    
+                };
+                ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
+                return true;
+            }
+            catch(Exception ex)
+            {
+                Trace.WriteLine(ex.Message);
+                return false;
+            }
+        }
+        #endregion
+
+        private static async ValueTask<KeyVaultSecret> GetClientIDSecret(string clientID, string location)
+        {   //Azure 金鑰庫處理
+            var s = await Task.Run(() =>
+            {
+                var secret = KeyVaultSecrets.GetOrAdd(clientID, (x) =>
+                {
+                    try
+                    {
+                        var sts = Enum.Parse<CoreServiceClient>(location, true);
+                        var scrtetstring = sts.GetDescriptionText().Split(",");
+                        //TODO 之後驗證端點用KnownAuthorityHosts取代,此SDK版本無支援
+                        var secret = new ClientSecretCredential(scrtetstring[0], scrtetstring[1], scrtetstring[2], new TokenCredentialOptions() { AuthorityHost = new Uri(scrtetstring[3]) });
+                        var client = new SecretClient(new Uri(((KeyVaultEndpoint)sts).GetDescriptionText()), secret);
+                        var clientSecret = client.GetSecretAsync(clientID).ConfigureAwait(false);
+                        return clientSecret.GetAwaiter().GetResult();
+                    }
+                    catch
+                    {
+                        return null;
+                    }
+                });
+                return secret;
+            });
+            return s;
+        }
+
+        public static bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
+        {
+            return true;
+            //if (expires != null)
+            //{
+            //    if (DateTime.UtcNow < expires)
+            //    {
+            //        return true;
+            //    }
+            //}
+
+            //return false;
+        }
+
+        private enum STSEndpoint
+        {
+            [Description("https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b")]
+            China,
+            [Description("https://login.microsoftonline.com/73a2bcc5-fe99-4566-aa8a-07e7bb287df1")]
+            Global
+        }
+
+        private enum STSScope
+        {
+            [Description("api://72643704-b2e7-4b26-b881-bd5865e7a7a5/.default")]
+            China,
+            [Description("api://8768b06f-c5c5-4b0c-abfb-d7ded354626d/.default")]
+            Global
+        }
+
+        private enum KeyVaultEndpoint
+        {
+            [Description("https://corekeyvaultcn.vault.azure.cn/")]
+            China,
+            [Description("https://corekeyvaultjp.vault.azure.net/")]
+            Global
+        }
+
+        private enum CoreServiceClient
+        {
+            [Description("4807e9cf-87b8-4174-aa5b-e76497d7392b,72643704-b2e7-4b26-b881-bd5865e7a7a5,tRYbDXtotEOe2Bbmo=[3h9Hbu_Trt:c6,https://login.partner.microsoftonline.cn")]
+            China,
+            [Description("73a2bcc5-fe99-4566-aa8a-07e7bb287df1,8768b06f-c5c5-4b0c-abfb-d7ded354626d,7=O./yws0L89WcEsece:9/4deJHP4E=F,https://login.microsoftonline.com/")]
+            Global
+        }
+    }
+}

+ 10 - 7
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusTree.cs

@@ -10,17 +10,20 @@ namespace TEAMModelOS.SDK.Models
         }
         public List<SyllabusTree> children { get; set; }
     }
-    public class SyllabusTreeNode{
+    public class SyllabusTreeNode {
+
+        public SyllabusTreeNode(){
+            trees = new List<SyllabusTree>();
+        }
+        public string  id { get; set; }
         /// <summary>
         /// 册别的id
         /// </summary>
-        public string id { get; set; }
+        public string volumeId { get; set; }
        // public string code { get; set; }
         public string scope { get; set; }
-        public SyllabusTreeNode() {
-            trees = new List<SyllabusTree>();
-        }
-        public List<SyllabusTree> trees { get; set; }
-    }
+        public  List<SyllabusTree>  trees  { get; set; }
 
+        public List<SyllabusAuth> auth { get; set; } = new List<SyllabusAuth>();
+    }
 }

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs

@@ -21,6 +21,7 @@ namespace TEAMModelOS.SDK.Models
         public List<Tnode> children { get; set; }
         public string volumeId { get; set; }
         public List<SyllabusAuth> auth { get; set; }
+        public string scope { get; set; }
 }
 
     /// <summary>

+ 3 - 0
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -13,7 +13,9 @@
   <ItemGroup>
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.2.0" />
     <PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
+    <PackageReference Include="Azure.Identity" Version="1.4.0" />
     <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
+    <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
     <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.5.1" />
     <PackageReference Include="Azure.Storage.Queues" Version="12.6.1" />
     <PackageReference Include="ClouDASLibx" Version="1.2.7" />
@@ -22,6 +24,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="5.0.4" />
+    <PackageReference Include="Microsoft.Identity.Client" Version="4.32.1" />
     <PackageReference Include="StackExchange.Redis" Version="2.2.4" />
     <PackageReference Include="SvgNet" Version="2.1.1" />
     <PackageReference Include="System.Drawing.Common" Version="5.0.2" />

+ 70 - 64
TEAMModelOS/Controllers/Analysis/AnalysisController.cs

@@ -538,7 +538,7 @@ namespace TEAMModelOS.Controllers.Analysis
                     {
                         //年级全科的pr
                         int index = stuGradeTotal.IndexOf(x.total);
-                        double GPR = stuCount > 0 ? Math.Floor(100 - (100 * (index + 1) - 50)*1.0 / stuCount) : 0;
+                        double GPR = stuCount > 0 ? Math.Floor(100 - (100 * (index + 1) - 50) * 1.0 / stuCount) : 0;
                         //double GPR = 100 - (100 * (index + 1) - 50) / stuCount;
                         x.gpr = GPR;
                         x.gsort = index + 1;
@@ -580,15 +580,16 @@ namespace TEAMModelOS.Controllers.Analysis
                 BadRequest(ex.StackTrace);
             }
 
-            var sub  = examResults.Select(e => new {
-                    id = e.id,
-                    name = info.subjects.FirstOrDefault(c => c.id == e.id).name,
-                    record = e.record,
-                    phc = e.phc,
-                    plc = e.plc,
-                    sRate = e.sRate,
-                    average = e.average,
-                    standard = e.standard
+            var sub = examResults.Select(e => new
+            {
+                id = e.id,
+                name = info.subjects.FirstOrDefault(c => c.id == e.id).name,
+                record = e.record,
+                phc = e.phc,
+                plc = e.plc,
+                sRate = e.sRate,
+                average = e.average,
+                standard = e.standard
             });
             /*var sub = info.subjects.Select(x => new
             {
@@ -1293,18 +1294,18 @@ namespace TEAMModelOS.Controllers.Analysis
                                 //单个认知层次得分情况
                                 scores += exam.studentScores[index][n];
                             }
-                           /* if (exam.studentScores[index].Sum() >= rhw && phCount < rhwCount && exam.studentScores[index][n] == 0)
-                            {
-                                rhwC++;
-                                phCount++;
-                                continue;
-                            }
-                            if (exam.studentScores[index].Sum() <= rhl && plCount < (exam.studentIds.Count - rhlCount) && exam.studentScores[index][n] == 0)
-                            {
-                                rhlC++;
-                                plCount++;
-                                continue;
-                            }*/
+                            /* if (exam.studentScores[index].Sum() >= rhw && phCount < rhwCount && exam.studentScores[index][n] == 0)
+                             {
+                                 rhwC++;
+                                 phCount++;
+                                 continue;
+                             }
+                             if (exam.studentScores[index].Sum() <= rhl && plCount < (exam.studentIds.Count - rhlCount) && exam.studentScores[index][n] == 0)
+                             {
+                                 rhlC++;
+                                 plCount++;
+                                 continue;
+                             }*/
                             //anwGPoint += exam.studentScores[index][n];
                         }
                         gPoint += point[n];
@@ -1469,7 +1470,7 @@ namespace TEAMModelOS.Controllers.Analysis
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
                     school = json.ToObject<School>();
-                }   
+                }
                 //School school = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<School>(code.ToString(), new PartitionKey($"Base"));
                 List<ExamResult> examResults = new List<ExamResult>();
                 var query = $"select c.id,c.name,c.subjectId,c.studentScores,c.studentIds,c.paper,c.classes from c where c.examId =  '{id}' ";
@@ -1512,33 +1513,13 @@ namespace TEAMModelOS.Controllers.Analysis
                     List<List<double>> classScores = new List<List<double>>();
                     //此处声明集合存储原本的成绩序列
                     List<List<double>> cScores = new List<List<double>>();
-                    foreach (ClassRange range in result.classes)
+                    /*var sc = result.classes.GroupBy(m => new { m.gradeId }).Select(c => new { gId = c.Key.gradeId, ranges = c.ToList() }).ToList();
+                    foreach (var aa in sc)
                     {
-                        List<double> scores = new List<double>();
-                        List<double> finalScores = new List<double>();
-                        if (!string.IsNullOrEmpty(range.gradeId))
+                        foreach (ClassRange range in aa.ranges)
                         {
-                            if (range.gradeId.Equals(gId.ToString(), StringComparison.OrdinalIgnoreCase))
-                            {
-                                ClassId.Add(range.id);
-                                cla.Add(range.id);
-                                double totalClass = 0;
-                                for (int i = range.range[0]; i <= range.range[1]; i++)
-                                {
-                                    totalClass += result.studentScores[i].Sum();
-                                    scores.Add(result.studentScores[i].Sum());
-                                    finalScores.Add(result.studentScores[i].Sum());
-                                    gradeScores.Add(result.studentScores[i].Sum());
-                                    totalGrade += result.studentScores[i].Sum();
-                                    stu.Add(result.studentIds[i]);
-                                }
-                                classScores.Add(scores);
-                                cScores.Add(finalScores);
-                                ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
-                                personCount.Add(range.range[1] - range.range[0] + 1);
-                            }
-                        }
-                        else {
+                            List<double> scores = new();
+                            List<double> finalScores = new();
                             ClassId.Add(range.id);
                             cla.Add(range.id);
                             double totalClass = 0;
@@ -1556,23 +1537,48 @@ namespace TEAMModelOS.Controllers.Analysis
                             ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
                             personCount.Add(range.range[1] - range.range[0] + 1);
                         }
-                        
+                        classAllAverage.Add(ClassAverage);
+                        gradeScores.Sort((s1, s2) => { return s2.CompareTo(s1); });
+                        indexClass = ClassId.IndexOf(cId.ToString());
+                        //单科成绩
+                        allList.Add(cScores[indexClass]);
+                        classScores[indexClass].Sort((s1, s2) => { return s2.CompareTo(s1); });
+                        mapClass.Add("scoreSum", result.paper.point.Sum());
+                        mapClass.Add("score", result.studentScores[index].Sum());
+                        mapClass.Add("classAverage", ClassAverage[indexClass]);
+                        mapClass.Add("classCount", personCount[indexClass]);
+                        mapClass.Add("classRank", classScores[indexClass].IndexOf(result.studentScores[index].Sum()));
+                        mapClass.Add("gradeAverage", result.studentIds.Count > 0 ? Math.Round(totalGrade * 1.0 / result.studentIds.Count, 2) : 0);
+                        mapClass.Add("gradeCount", result.studentIds.Count);
+                        mapClass.Add("gradeRank", gradeScores.IndexOf(result.studentScores[index].Sum()));
+                        averageMap.Add(mapClass);
                     }
-                    classAllAverage.Add(ClassAverage);
-                    gradeScores.Sort((s1, s2) => { return s2.CompareTo(s1); });
-                    indexClass = ClassId.IndexOf(cId.ToString());
-                    //单科成绩
-                    allList.Add(cScores[indexClass]);
-                    classScores[indexClass].Sort((s1, s2) => { return s2.CompareTo(s1); });
-                    mapClass.Add("scoreSum", result.paper.point.Sum());
-                    mapClass.Add("score", result.studentScores[index].Sum());
-                    mapClass.Add("classAverage", ClassAverage[indexClass]);
-                    mapClass.Add("classCount", personCount[indexClass]);
-                    mapClass.Add("classRank", classScores[indexClass].IndexOf(result.studentScores[index].Sum()));
-                    mapClass.Add("gradeAverage", result.studentIds.Count > 0 ? Math.Round(totalGrade * 1.0 / result.studentIds.Count, 2) : 0);
-                    mapClass.Add("gradeCount", result.studentIds.Count);
-                    mapClass.Add("gradeRank", gradeScores.IndexOf(result.studentScores[index].Sum()));
-                    averageMap.Add(mapClass);
+*/
+                    foreach (ClassRange range in result.classes)
+                    {
+                        List<double> scores = new List<double>();
+                        List<double> finalScores = new List<double>();
+
+                        ClassId.Add(range.id);
+                        cla.Add(range.id);
+                        double totalClass = 0;
+                        for (int i = range.range[0]; i <= range.range[1]; i++)
+                        {
+                            totalClass += result.studentScores[i].Sum();
+                            scores.Add(result.studentScores[i].Sum());
+                            finalScores.Add(result.studentScores[i].Sum());
+                            gradeScores.Add(result.studentScores[i].Sum());
+                            totalGrade += result.studentScores[i].Sum();
+                            stu.Add(result.studentIds[i]);
+                        }
+                        classScores.Add(scores);
+                        cScores.Add(finalScores);
+                        ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
+                        personCount.Add(range.range[1] - range.range[0] + 1);
+
+
+                    }
+
                 }
                 //处理班级/年级全科均分
                 List<double> AllAverage = new List<double>();

+ 10 - 67
TEAMModelOS/Controllers/Syllabus/ShareController.cs

@@ -304,11 +304,10 @@ namespace TEAMModelOS.Controllers
         {
             try
             {
-                List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
+                List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
                 Volume volume;
                 var client = _azureCosmos.GetCosmosClient();
                 string code = null;
-                SyllabusTreeNode tree = null;
                 List<string> sid = new List<string>();
                 request.syllabusId.ForEach(x => { sid.Add($"'{x}'"); });
                 var sidSql= string.Join(",", sid);
@@ -319,86 +318,31 @@ namespace TEAMModelOS.Controllers
                     await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
                     {
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
+
                     }
                     volume = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
-                    tree = new SyllabusTreeNode() { id = request.volumeId, scope = "school", trees = new List<SyllabusTree>() };
+                    
                 }
                 else if (request.scope == "private")
                 {
                     code = request.issuer;
-                    var queryslt = $"SELECT  value(c) FROM c ";
+                    var queryslt = $"SELECT  value(c) FROM c where c.id in ({sidSql})";
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
                     {
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
                     }
                     volume = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
-                    tree = new SyllabusTreeNode() { id = request.volumeId, scope = "private", trees = new List<SyllabusTree>() };
                 }
                 else
                 {
                     return BadRequest();
                 }
-                //bool coedit = false;
-                //bool share = false;
-                //SyllabusTreeNode  etree = null;
-                //if (syllabus.auth.IsNotEmpty())
-                //{
-                //    foreach (var x in syllabus.auth)
-                //    {
-                //        if ($"Share-{x.tmdid }" == request.code)
-                //        {
-                //            coedit = x.coedit;
-                //            share = x.share;
-                //            break;
-                //        }
-                //    }
-                //}
-                //if (coedit)
-                //{
-                //    var treechd = SyllabusService.ListToTree(syllabus.children);
-                //    etree = new SyllabusTreeNode() {
-                //        id = syllabus.id,
-                //        trees = treechd
-                //    };
-                //}
-                //SyllabusTreeNode stree = new SyllabusTreeNode();
-                //if (share && snodes.IsNotEmpty())
-                //{
-                //    stree = new SyllabusTreeNode()
-                //    {
-                //        id = syllabus.id,
-                //    };
-                //    snodes.ForEach(x =>
-                //    {
-                //        SyllabusTree syllabusTree = null;
-                //        foreach (var node in syllabus.children)
-                //        {
-                //            if (node.id == x)
-                //            {
-                //                syllabusTree = new SyllabusTree
-                //                {
-                //                    id = node.id,
-                //                    pid = node.pid,
-                //                    order = node.order,
-                //                    rnodes = node.rnodes,
-                //                    cids = node.cids
-                //                };
-                //                break;
-                //            }
-                //        }
-                //        HashSet<Tnode> newNodes = new HashSet<Tnode>();
-                //        SyllabusService.GetNewNode(syllabus.children, x, newNodes);
-                //        var trees = SyllabusService.ListToTree(newNodes.ToList());
-                //        if (syllabusTree != null)
-                //        {
-                //            syllabusTree.children.AddRange(trees);
-                //            stree.trees.Add(syllabusTree);
-                //        }
-                //    });
-                //}
-                return Ok(new { tree });
+                
+                return Ok(new { volume,tree= treeNodes });
             }
             catch (Exception ex) {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/view()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
@@ -417,7 +361,6 @@ namespace TEAMModelOS.Controllers
         public async Task<IActionResult> Qrcode(Favorite request)
         {
             return Ok();
-
         }
     }
    

+ 103 - 267
TEAMModelOS/Controllers/Syllabus/SyllabusController.cs

@@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using Azure.Cosmos;
 using TEAMModelOS.Services.Common;
+using Microsoft.Extensions.Options;
 
 namespace TEAMModelOS.Controllers
 {
@@ -28,163 +29,15 @@ namespace TEAMModelOS.Controllers
     public class SyllabusController : ControllerBase
     {
         private readonly AzureCosmosFactory _azureCosmos;
-
-
-        public SyllabusController(AzureCosmosFactory azureCosmos)
+        private readonly Option _option;
+        private readonly DingDing _dingDing;
+        public SyllabusController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
         }
 
-        /*
-        {
-    "id": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-    "scope": "school",
-    "trees": [
-        {
-            "children": [
-                {
-                    "children": [],
-                    "id": "AC4BA269-541B-4DFC-92A5-D773068A6439",
-                    "pid": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
-                    "order": 0,
-                    "rnodes": [
-                        {
-                            "type": "doc",
-                            "id": "a2bee388-5584-72cc-1d9a-d8a77d255364",
-                            "code": "hbcn",
-                            "scope": "private",
-                            "cntr": "1595321354",
-                            "link": [
-                                "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/2020智慧課堂與智慧教研.pptx"
-                            ],
-                            "title": "2020智慧課堂與智慧教研.pptx"
-                        }
-                    ],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "1-1 新冠疫情小贴士"
-                }
-            ],
-            "id": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
-            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-            "order": 0,
-            "rnodes": [
-                {
-                    "type": "doc",
-                    "id": "cf1b4d21-66e1-e6c7-c944-30a03e419fa6",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/IES5试卷模板制作说明(终).pdf"
-                    ],
-                    "title": "IES5试卷模板制作说明(终).pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "f3e82595-7340-a5fe-1004-04538ca09b86",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/111.pdf"
-                    ],
-                    "title": "111.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "e1b31639-dad9-9efb-020b-159dd045f238",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6789.pdf"
-                    ],
-                    "title": "6789.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "aaeb4b5c-0450-cb4d-a1ac-244f3d115c4a",
-                    "code": "hbcn",
-                    "scope": "private",
-                    "cntr": "1595321354",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/422北京培新活动邀请函.pdf"
-                    ],
-                    "title": "422北京培新活动邀请函.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "c5f5838c-5c2f-5e9b-a734-a473cd2cad2e",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6666.pdf"
-                    ],
-                    "title": "6666.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "4a106c8e-6831-5f4b-4ec3-3c18f778cab2",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6.pdf"
-                    ],
-                    "title": "6.pdf"
-                }
-            ],
-            "cids": [
-                "AC4BA269-541B-4DFC-92A5-D773068A6439"
-            ],
-            "creatorId": null,
-            "updateTime": 0,
-            "title": "第一单元 新冠疫情防控"
-        },
-        {
-            "children": [
-                {
-                    "children": [],
-                    "id": "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
-                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-                    "order": 0,
-                    "rnodes": [],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "2-1 全球醍摩豆智慧教育研究院"
-                },
-                {
-                    "children": [],
-                    "id": "3CAD52BE-67B6-4EDB-8EFB-9122960D816A",
-                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-                    "order": 1,
-                    "rnodes": [],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "2-2 醍摩豆研究院课程体系"
-                }
-            ],
-            "id": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-            "order": 1,
-            "rnodes": [],
-            "cids": [
-                "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
-                "3CAD52BE-67B6-4EDB-8EFB-9122960D816A"
-            ],
-            "creatorId": null,
-            "updateTime": 0,
-            "title": "第二单元 醍摩豆手册学习"
-        }
-    ]
-}
-              */
-
         /// <summary>
         /// 批量保存或更新课纲
         /// 
@@ -193,38 +46,96 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-tree")]
-        public async Task<IActionResult> SaveOrUpdateAsTree(SyllabusTreeNode request)
+        public async Task<IActionResult> SaveOrUpdateAsTree(List<SyllabusTreeNode> request)
         {
-            List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
-            foreach (var tree in request.trees) {
-                Syllabus syllabus = new Syllabus();
+            foreach (SyllabusTreeNode syllabusTree in request) {
                 List<Tnode> nodes = new List<Tnode>();
-                SyllabusService.TreeToList(new List<SyllabusTree> { tree }, nodes);
-                syllabus.children = nodes;
-                syllabus.id = !string.IsNullOrEmpty(tree.id) ? tree.id : Guid.NewGuid().ToString();
-                syllabus.code = $"Syllabus-{request.id}";
-                syllabus.pk = "Syllabus";
-                syllabus.ttl = -1;
-                syllabus.volumeId = request.id;
-                if (request.scope == "school")
+                SyllabusService.TreeToList(syllabusTree.trees, nodes);
+                if (!string.IsNullOrEmpty(syllabusTree.id))
                 {
-                    await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{request.id}"));
+                    Syllabus syllabus = null;
+                    if (syllabusTree.scope == "school")
+                    {
+                        try
+                        {
+                            syllabus = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>(syllabusTree.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        catch
+                        {
+                        }
+                        if (syllabus == null)
+                        {
+                            syllabus = new Syllabus();
+                            syllabus.id = syllabusTree.id;
+                            syllabus.children = nodes;
+                            syllabus.code = $"Syllabus-{syllabusTree.volumeId}";
+                            syllabus.pk = "Syllabus";
+                            syllabus.ttl = -1;
+                            syllabus.volumeId = syllabusTree.volumeId;
+                            syllabus.scope = syllabusTree.scope;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        else
+                        {
+                            syllabus.children = nodes;
+                            syllabusTree.auth = syllabus.auth;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabus,syllabus.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+
+                    }
+                    else {
+                        try
+                        {
+                            syllabus = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Syllabus>(syllabusTree.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        catch
+                        {
+                        }
+                        if (syllabus == null)
+                        {
+                            syllabus = new Syllabus();
+                            syllabus.id = syllabusTree.id;
+                            syllabus.children = nodes;
+                            syllabus.code = $"Syllabus-{syllabusTree.volumeId}";
+                            syllabus.pk = "Syllabus";
+                            syllabus.ttl = -1;
+                            syllabus.volumeId = syllabusTree.volumeId;
+                            syllabus.scope = syllabusTree.scope;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        else
+                        {
+                            syllabus.children = nodes;
+                            syllabusTree.auth = syllabus.auth;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                    }
                 }
-                else
-                {
-                    await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{request.id}"));
+                else {
+                    string id = Guid.NewGuid().ToString();
+                    syllabusTree.id = id;
+                    Syllabus syllabus = new Syllabus {
+                        id = id,
+                        code = $"Syllabus-{syllabusTree.volumeId}",
+                        pk = "Syllabus",
+                        ttl = -1,
+                        volumeId=syllabusTree.volumeId,
+                        children=nodes,
+                        scope = syllabusTree.scope
+                    };
+                    if (syllabusTree.scope == "school") {
+                        await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                    } else {
+                        await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                    }
                 }
-                List<SyllabusTree> trees = SyllabusService.ListToTree(nodes);
-                tts.Add(trees);
             }
-
-            request.trees = new List<SyllabusTree>() ;
-            tts.ForEach(x => request.trees.AddRange(x));
             return Ok(request);
         }
 
- 
-
+        /*
+            {"code":"册别code:0baf00db-0768-4b62-a8f7-280f6bcebf71","scope":"school"}
+         */
         /// <summary>
         /// 查找课纲 
         /// </summary>
@@ -237,39 +148,41 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
             if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
             if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
-            SyllabusTreeNode tree = null;
-            string vcode = null;
-            List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
+           
+            List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
             try {
                 if (scope.ToString().Equals("school"))
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Syllabus>(queryText: $"select value(c) from c ",
                     requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
                     {
-                        vcode = item.volumeId;
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope =item.scope, trees = trees ,volumeId=item.volumeId,auth=item.auth};
+                        treeNodes.Add(tree);
                     }
-                    tree = new SyllabusTreeNode() { id = vcode,  scope = "school", trees= new List<SyllabusTree>() };
-                    tts.ForEach(x => tree.trees.AddRange(x));
+                    
                 }
                 else
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Syllabus>(queryText: $"select value(c) from c ",
-                    requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
+                     requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
                     {
-                        vcode = item.volumeId;
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
                     }
-                    tree = new SyllabusTreeNode() { id = vcode, scope = "private", trees = new List<SyllabusTree>() };
-                    tts.ForEach(x => tree.trees.AddRange(x)); 
                 }
-                return Ok(new { tree });
+                return Ok(new { tree= treeNodes });
             } catch (Exception ex) {
-                return Ok(new { tree});
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/syllabus/find-id\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return Ok(new { tree= treeNodes });
             }
         }
+
+        /*
+            {"id":"章节id","code":"册别id","scope":"school/private"}
+         */
+
         /// <summary>
         /// 删除章节
         /// </summary>
@@ -294,82 +207,5 @@ namespace TEAMModelOS.Controllers
                 return Ok(new { code = response.Status });
             }
         }
-
-
-     
-
-        private async Task<List<Tnode>> FindByPid(Tnode data, List<Tnode> nodes, List<Tnode> nodedata)
-        {
-            foreach (Tnode syllabus in nodedata)
-            {
-                if (syllabus.pid == data.id)
-                {
-                    nodes.Add(syllabus);
-                    await FindByPid(syllabus, nodes, nodedata);
-                }
-            }
-            return nodes;
-        }
-
-        
-        //private async Task<List<Syllabus>> SaveOrUpdateToNodes(List<Tnode> syllabusNodes)
-        //{
-        //    List<Syllabus> syllabuses;
-
-        //   var  data = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>(  syllabusNodes[0].code ,new Azure.Cosmos.PartitionKey(""));
-        //    if (data.IsEmpty())
-        //    {
-        //        var syllabus = new Syllabus { id = syllabusNodes[0].code, code = syllabusNodes[0].code, children = new List<SyllabusNode>() };
-        //        await _azureCosmos.SaveOrUpdate(syllabus);
-        //        data = new List<Syllabus>() { syllabus };
-        //    }
-        //    List<SyllabusNode> syllabusNodes1 = new List<SyllabusNode>();
-        //    if (data.IsNotEmpty())
-        //    {
-        //        syllabusNodes1.AddRange(data[0].children);
-        //        //replace
-        //        for (int i = 0; i < data[0].children.Count; i++)
-        //        {
-        //            for (int j = 0; j < syllabusNodes.Count; j++)
-        //            {
-        //                if (data[0].children[i].id == syllabusNodes[j].id && data[0].children[i].code == syllabusNodes[j].code)
-        //                {
-        //                    syllabusNodes1.Remove(data[0].children[i]);
-        //                    syllabusNodes1.Add(syllabusNodes[j]);
-        //                    syllabusNodes.Remove(syllabusNodes[j]);
-        //                }
-        //            }
-        //        }
-        //        data[0].children = syllabusNodes1;
-
-        //        //新增
-        //        data[0].children.AddRange(syllabusNodes);
-
-        //        if (data[0].children.IsNotEmpty())
-        //        {
-        //            var len = data[0].children.Count;
-        //            for (int i = 0; i < len; i++)
-        //            {
-        //                if (data[0].children[i].items.IsNotEmpty())
-        //                {
-        //                    data[0].children[i].item = true;
-        //                }
-        //                if (data[0].children[i].resources.IsNotEmpty())
-        //                {
-        //                    data[0].children[i].resource = true;
-        //                }
-        //            }
-        //        }
-        //        syllabuses = await _azureCosmos.SaveOrUpdateAll<Syllabus>(data);
-
-        //    }
-        //    else
-        //    {
-        //        throw new BizException("保存失败", ResponseCode.FAILED);
-
-        //    }
-
-        //    return syllabuses;
-        //}
     }
 }