Browse Source

处理GroupList

CrazyIter_Bin 3 years ago
parent
commit
82ad8fabe1

+ 1 - 22
TEAMModelFunction/ActivityHttpTrigger.cs

@@ -414,28 +414,7 @@ namespace TEAMModelFunction
             return new OkObjectResult(new { });
         }
         
-        /// <summary>
-        /// 设置问卷调查未初始化学生列表的业务
-        /// </summary>
-        /// <param name="req"></param>
-        /// <param name="log"></param>
-        /// <returns></returns>
-        [FunctionName("refresh-stu-activity")]
-        public async Task<IActionResult> RefreshStuActivity(
-            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
-            ILogger log)
-        {
-            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
-            dynamic json = JsonSerializer.Deserialize<dynamic>(requestBody);
-            string id = json.id;
-            string code = json.code;
-            if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(code)) {
-                return new BadRequestResult();
-            }
-            var client = _azureCosmos.GetCosmosClient();
-            await TriggerStuActivity.RefreshStuActivity(client, _dingDing, id, code);
-            return new OkObjectResult(new {code=200 });
-        }
+       
         /// <summary>
         ///获取单个目录的大小,用于获取评测,试题,试卷,问卷,投票等 文件层级超过两层的文件。
         ///例如 /exam/uuid/xxx  /item/uuid/xxx   /paper/uuid/xxx  /vote/uuid/xxx  /suervy/uuid/xxx

+ 41 - 0
TEAMModelFunction/MonitorServicesBus.cs

@@ -16,6 +16,7 @@ using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.Services.Common;
+using TEAMModelOS.SDK.Models.Service;
 
 namespace TEAMModelFunction
 {
@@ -387,7 +388,47 @@ namespace TEAMModelFunction
                 await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListServiceBus-StuList\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.成都开发測試群組);
             }
         }
+        /// <summary>
+        /// 完善课程变更,StuListChange,  originCode是学校编码 则表示名单是学校自定义名单,如果是tmdid则表示醍摩豆的私有名单,scope=school,private。
+        /// </summary>
+        /// <data msg>
+        /// CourseChange
+        ///// </data>
+        /// <param name="msg"></param>
+        /// <returns></returns>
+        [FunctionName("GroupChange")]
+        public async Task GroupChange([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "group-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
+        {
+            var client = _azureCosmos.GetCosmosClient();
+            try
+            {
+                var jsonMsg = JsonDocument.Parse(msg);
+                GroupChange groupChange = msg.ToObject<GroupChange>();
+                //名单变动修改学生课程关联信息
+                //await StuListService.FixStuCourse(client, stuListChange);
+                //Vote投票 Survey问卷 Exam评测 Learn学习活动 Homework作业活动
+                //名单变动修改学生问卷关联信息
+                await GroupChangeService.FixActivity(client, _dingDing, groupChange, "Survey");
+                //名单变动修改学生投票关联信息
+                await GroupChangeService.FixActivity(client, _dingDing, groupChange, "Vote");
+                //名单变动修改学生评测关联信息
+                await GroupChangeService.FixActivity(client, _dingDing, groupChange, "Exam");
+                //TODO学习活动
+                //await FixActivity(client, stuListChange, "Learn");
+                //TODO作业活动
+                await GroupChangeService.FixActivity(client, _dingDing, groupChange, "Homework");
 
+                if (groupChange.type == null || !groupChange.type.Equals("research"))
+                {
+                    //课程名单变动修改学生课程关联信息
+                    await GroupChangeService.FixStuCourse(client, _dingDing, groupChange);
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListServiceBus-StuList\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.成都开发測試群組);
+            }
+        }
         [FunctionName("ItemCond")]
         public async Task ItemCond([ServiceBusTrigger("%Azure:ServiceBus:ItemCondQueue%", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
         {

+ 43 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/GroupChange.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models
+{
+    public class GroupChange
+    { /// <summary>
+      /// 醍摩豆学生名单加入的成员
+      /// </summary>
+        public List<Member> tmdjoin { get; set; } = new List<Member>();
+        /// <summary>
+        /// 醍摩豆学生名单离开的成员
+        /// </summary>
+        public List<Member> tmdleave { get; set; } = new List<Member>();
+        /// <summary>
+        /// 学校学生名单加入的成员
+        /// </summary>
+        public List<Member> stujoin { get; set; } = new List<Member>();
+        /// <summary>
+        /// 学校学生名单离开的成员
+        /// </summary>
+        public List<Member> stuleave { get; set; } = new List<Member>();
+
+        /// <summary>
+        /// 学校教师名单加入的成员
+        /// </summary>
+        public List<Member> tchjoin { get; set; } = new List<Member>();
+        /// <summary>
+        /// 学校教师名单离开的成员
+        /// </summary>
+        public List<Member> tchleave { get; set; } = new List<Member>();
+        public string listid { get; set; }
+        /// <summary>
+        /// 分区
+        /// </summary>
+        public string scope { get; set; }
+        public string originCode { get; set; }
+        public string school { get; set; }
+        public string creatorId { get; set; }
+        public string type { get; set; }
+    }
+}

+ 2 - 5
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/ListChange.cs

@@ -2,7 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Text;
 
-namespace TEAMModelOS.SDK.Models.Cosmos.Common
+namespace TEAMModelOS.SDK.Models
 {
     public class ListChange
     {
@@ -40,9 +40,6 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         public string school { get; set; }
         public string creatorId { get; set; }
         public string type { get; set; }
-        /// <summary>
-        /// 当状态码大于等于0则表示正常操作,只有当状态码=-1的时候
-        /// </summary>
-        public int listStatus { get; set; } = 1;
+       
     }
 }

+ 57 - 0
TEAMModelOS.SDK/Models/Service/AccountHttpService.cs

@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Extension;
+
+
+namespace TEAMModelOS.SDK.Models.Service
+{
+    public class AccountHttpService
+    {
+        private readonly HttpClient _httpClient;
+        public AccountHttpService(HttpClient httpClient)
+        {
+            _httpClient = httpClient;
+        }
+        /// <summary>
+        ///  隐式登录
+        /// </summary>
+        /// <param name="clientID"></param>
+        /// <param name="clientSecret"></param>
+        /// <param name="location"></param>
+        /// <param name="url"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public async Task<(int code ,string content)> Implicit(string clientID, string clientSecret, string location, string url,Dictionary<string,string> data)
+        {
+            if (location.Contains("China"))
+            {
+                location = "China";
+            }
+            else if (location.Contains("Global"))
+            {
+                location = "Global";
+            }
+            var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location);
+            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+            HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync(url, data);
+            if (responseMessage.StatusCode == HttpStatusCode.OK)
+            {
+                string content=await responseMessage.Content.ReadAsStringAsync();
+                return (200,content);
+            }
+            else if (responseMessage.StatusCode == HttpStatusCode.Unauthorized)
+            {
+                return (401,null);
+            }
+            else
+            {
+                return (500,null);
+            }
+        }
+    }
+}

+ 262 - 0
TEAMModelOS.SDK/Models/Service/GroupChangeService.cs

@@ -0,0 +1,262 @@
+using Azure.Cosmos;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using HTEXLib.COMM.Helpers;
+namespace TEAMModelOS.SDK.Models.Service
+{
+   public class GroupChangeService
+    {
+        public static async Task FixActivity(CosmosClient client, DingDing _dingDing, GroupChange groupChange, string type)
+        {
+            try
+            {
+                var query = $"SELECT distinct c.owner, c.id,c.code, c.classes,c.stuLists,c.subjects,c.progress,c.scope,c.startTime,c.school,c.creatorId,c.name,c.pk ,c.endTime   FROM c  where  c.pk='{type}' " +
+                    $" and (( array_contains(c.classes,'{groupChange.listid}')) or ( array_contains(c.stuLists,'{groupChange.listid}'))or ( array_contains(c.tchLists,'{groupChange.listid}')))";
+                //$"and A1 in('{groupChange.listid}') ";
+                List<MQActivity> datas = new List<MQActivity>();
+                if (groupChange.scope.Equals("school", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(groupChange.school))
+                {
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<MQActivity>(queryText: query,
+                       requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"{type}-{groupChange.school}") }))
+                    {
+                        datas.Add(item);
+                    }
+                    ///还要处理该学校每个老师发布的班级的
+                    List<SchoolTeacher> teachers = new List<SchoolTeacher>();
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<SchoolTeacher>(queryText: $"SELECT c.id, c.name FROM c",
+                        requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{groupChange.school}") }))
+                    {
+                        teachers.Add(item);
+                    }
+                    foreach (var techer in teachers)
+                    {
+                        var queryTech = $"SELECT distinct c.owner, c.id,c.code, c.classes,c.stuLists,c.subjects,c.progress,c.scope,c.startTime,c.school,c.creatorId,c.name,c.pk ,c.endTime   FROM c " +
+                            $" where c.school='{groupChange.school}'   and   c.pk='{type}'" +
+                            $" and (( array_contains(c.classes,'{groupChange.listid}')) or ( array_contains(c.stuLists,'{groupChange.listid}')))";
+                        //  $" and A1 in('{groupChange.listid}') ";
+                        await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<MQActivity>(queryText: queryTech,
+                            requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"{type}-{techer.id}") }))
+                        {
+                            datas.Add(item);
+                        }
+                    }
+                }
+                if (groupChange.scope.Equals("private", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(groupChange.creatorId))
+                {
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<MQActivity>(queryText: query,
+                        requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"{type}-{groupChange.creatorId}") }))
+                    {
+                        datas.Add(item);
+                    }
+                }
+                long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                foreach (MQActivity activity in datas)
+                {
+                    //已经完结的不再允许加入,还未开始的。
+                    if (activity.progress.Equals("finish") || activity.progress.Equals("pending"))
+                    {
+                        continue;
+                    }
+                    List<string> classes = ExamService.getClasses(activity.classes, activity.stuLists);
+                    //stujoin新加入名单的
+                    foreach (Member member in groupChange.stujoin)
+                    {
+                        var stucourse = new StuActivity
+                        {
+                            id = activity.id,
+                            scode = activity.code,
+                            name = activity.name,
+                            code = $"Activity-{member.code.Replace("Base-", "")}-{member.id}",
+                            scope = activity.scope,
+                            school = activity.school,
+                            creatorId = activity.creatorId,
+                            pk = "Activity",
+                            type = type,
+                            subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                            startTime = activity.startTime,
+                            endTime = activity.endTime,
+                            blob = activity.blob,
+                            owner = activity.owner,
+                            createTime = nowtime,
+                            taskStatus = -1,
+                            classIds = classes
+                        };
+                        await client.GetContainer(Constant.TEAMModelOS, "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                    }
+
+                    //tmdjoin新加入的
+                    foreach (Member member in groupChange.tmdjoin)
+                    {
+                        var stucourse = new StuActivity
+                        {
+                            id = activity.id,
+                            scode = activity.code,
+                            name = activity.name,
+                            code = $"Activity-{member.id}",
+                            scope = activity.scope,
+                            school = activity.school,
+                            creatorId = activity.creatorId,
+                            pk = "Activity",
+                            type = type,
+                            subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                            startTime = activity.startTime,
+                            endTime = activity.endTime,
+                            blob = activity.blob,
+                            owner = activity.owner,
+                            createTime = nowtime,
+                            taskStatus = -1,
+                            classIds = classes
+                        };
+                        await client.GetContainer(Constant.TEAMModelOS, "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                    }
+                    //tchjoin新加入的
+                    foreach (Member member in groupChange.tchjoin)
+                    {
+                        var stucourse = new StuActivity
+                        {
+                            id = activity.id,
+                            scode = activity.code,
+                            name = activity.name,
+                            code = $"Activity-{member.id}",
+                            scope = activity.scope,
+                            school = activity.school,
+                            creatorId = activity.creatorId,
+                            pk = "Activity",
+                            type = type,
+                            subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                            startTime = activity.startTime,
+                            endTime = activity.endTime,
+                            blob = activity.blob,
+                            owner = activity.owner,
+                            createTime = nowtime,
+                            taskStatus = -1,
+                            classIds = classes
+                        };
+                        await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                    }
+                    foreach (Member member in groupChange.stuleave)
+                    {
+                        try
+                        {
+
+                            await client.GetContainer(Constant.TEAMModelOS, "Student").DeleteItemAsync<StuActivity>(activity.id, new PartitionKey($"Activity-{member.code.Replace("Base-", "")}-{member.id}"));
+                        }
+                        catch (CosmosException  )
+                        {
+                            continue;
+                          // 继续执行 删除失败
+                        }
+                    }
+                    foreach (Member member in groupChange.tmdleave)
+                    {
+                        try
+                        {
+
+                            await client.GetContainer(Constant.TEAMModelOS, "Student").DeleteItemAsync<StuActivity>(activity.id, new PartitionKey($"Activity-{member.id}"));
+                        }
+                        catch (CosmosException  )
+                        {
+                            continue;
+                            // 继续执行 删除失败
+                        }
+                    }
+                    foreach (Member member in groupChange.tchleave)
+                    {
+                        try
+                        {
+                            await client.GetContainer(Constant.TEAMModelOS, "Teacher").DeleteItemAsync<StuActivity>(activity.id, new PartitionKey($"Activity-{member.id}"));
+                        }
+                        catch (CosmosException  )
+                        {
+                            continue;
+                            // 继续执行 删除失败
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListService-FixActivity\n{ex.Message}{ex.StackTrace}{groupChange.ToJsonString()}{type}", GroupNames.醍摩豆服務運維群組);
+            }
+        }
+        public static async Task FixStuCourse(CosmosClient client, DingDing _dingDing, GroupChange groupChange)
+        {
+            //1.查找学校或教师的课程是否包含该名单的课程。
+            var query = $"select distinct c.code,c.id,c.no,c.name,c.scope, c.creatorId,c.school from c join A0 in c.schedule where A0.stulist = '{groupChange.listid}'";
+            List<Course> courses = new List<Course>();
+            if (groupChange.scope.Equals("school") && !string.IsNullOrEmpty(groupChange.school))
+            {
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Course>(queryText: query,
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Course-{groupChange.school}") }))
+                {
+                    courses.Add(item);
+                }
+            }
+            if (groupChange.scope.Equals("private") && !string.IsNullOrEmpty(groupChange.creatorId))
+            {
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Course>(queryText: query,
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Course-{groupChange.creatorId}") }))
+                {
+                    courses.Add(item);
+                }
+            }
+            long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+            // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListService-FixStuCourse\n名单发生变更 需要处理的课程\n{courses.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+            //2.获取课程的id 并尝试添加或移除对应的学生课程记录StuCourse。
+            foreach (var course in courses)
+            {
+                //学生新加入名单的
+                foreach (Member member in groupChange.stujoin)
+                {
+                    var stucourse = new StuCourse
+                    {
+                        id = course.id,
+                        scode = course.code,
+                        name = course.name,
+                        code = $"StuCourse-{member.code.Replace("Base-", "")}-{member.id}",
+                        scope = course.scope,
+                        school = course.school,
+                        creatorId = course.creatorId,
+                        pk = "StuCourse",
+                        stulist = new List<string> { groupChange.listid },
+                        createTime = nowtime
+                    };
+                    // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListService-FixStuCourse\n名单发生变更 新建课程中间表\n{stucourse.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                    await client.GetContainer(Constant.TEAMModelOS, "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                }
+                //tmd新加入的
+                foreach (Member member in groupChange.tmdjoin)
+                {
+                    var stucourse = new StuCourse
+                    {
+                        id = course.id,
+                        scode = course.code,
+                        name = course.name,
+                        code = $"StuCourse-{member.id}",
+                        scope = course.scope,
+                        school = course.school,
+                        creatorId = course.creatorId,
+                        pk = "StuCourse",
+                        stulist = new List<string> { groupChange.listid },
+                        createTime = nowtime
+                    };
+                    // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListService-FixStuCourse\n名单发生变更 新建课程中间表\n{stucourse.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                    await client.GetContainer(Constant.TEAMModelOS, "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                }
+                //移除名单的。 在点击相关的课程,再去二次校验是否存在,不存在则再去删除。
+                foreach (var delStu in groupChange.stuleave)
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Student").DeleteItemStreamAsync(course.id, new PartitionKey($"StuCourse-{delStu.code.Replace("Base-", "")}-{delStu.id}"));
+                }
+                foreach (var delTmd in groupChange.tmdleave)
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Student").DeleteItemStreamAsync(course.id, new PartitionKey($"StuCourse-{delTmd}"));
+                }
+            }
+        }
+    }
+}

+ 26 - 23
TEAMModelOS/Controllers/Item/ItemController.cs

@@ -119,30 +119,33 @@ namespace TEAMModelOS.Controllers
                     { 
                         string  sql =   string.Join(',', nocachePeriods.Select(x => $"'{x}'"));
                         nocachePeriodsql = $" and  c.periodId in ({sql})";
-                    }
-
-                    List <ItemInfo> items = new List<ItemInfo>();
-                    var queryslt = $"SELECT c.gradeIds,c.subjectId,c.periodId,c.type,c.level,c.field,c.scope FROM c where    c.pid= null  {nocachePeriodsql}";
-                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{schoolCode}") }))
-                    {
-                        items.Add(item);
-                    }
-                    var list =   items.GroupBy(x => x.periodId).Select(y => new { key = y.Key, list = y.ToList() }).ToList();
-                    foreach (var z in list) {
-                        ItemCond cond = new ItemCond() { id = z.key, code = $"ItemCond-{school.id}", pk = "ItemCond", ttl = -1, count = z.list.Count, grades = new List<GradeCount>(), subjects = new List<SubjectCount>() };
-                        z.list.ForEach(y =>
+                        List<ItemInfo> items = new List<ItemInfo>();
+                        var queryslt = $"SELECT c.gradeIds,c.subjectId,c.periodId,c.type,c.level,c.field,c.scope FROM c where    c.pid= null  {nocachePeriodsql}";
+                        await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{schoolCode}") }))
                         {
-                            ItemService.CountItemCond(y, null, cond);
-                        });
-                        await _azureRedis.GetRedisClient(8).HashSetAsync($"ItemCond:{schoolCode}", $"{z.key}", cond.ToJsonString());
-                        itemConds.Add(cond);
-                    }
-                    if (school != null) {
-                        foreach (var period in school.period) {
-                           var cond= itemConds.Find(x => x.id.Equals(period.id));
-                            if (cond == null) {
-                                ItemCond condn = new ItemCond() { id = period.id, code = $"ItemCond-{school.id}", pk = "ItemCond", ttl = -1, count = 0, grades = new List<GradeCount>(), subjects = new List<SubjectCount>() };
-                                itemConds.Add(condn);
+                            items.Add(item);
+                        }
+                        var list = items.GroupBy(x => x.periodId).Select(y => new { key = y.Key, list = y.ToList() }).ToList();
+                        foreach (var z in list)
+                        {
+                            ItemCond cond = new ItemCond() { id = z.key, code = $"ItemCond-{school.id}", pk = "ItemCond", ttl = -1, count = z.list.Count, grades = new List<GradeCount>(), subjects = new List<SubjectCount>() };
+                            z.list.ForEach(y =>
+                            {
+                                ItemService.CountItemCond(y, null, cond);
+                            });
+                            await _azureRedis.GetRedisClient(8).HashSetAsync($"ItemCond:{schoolCode}", $"{z.key}", cond.ToJsonString());
+                            itemConds.Add(cond);
+                        }
+                        if (school != null)
+                        {
+                            foreach (var period in school.period)
+                            {
+                                var cond = itemConds.Find(x => x.id.Equals(period.id));
+                                if (cond == null)
+                                {
+                                    ItemCond condn = new ItemCond() { id = period.id, code = $"ItemCond-{school.id}", pk = "ItemCond", ttl = -1, count = 0, grades = new List<GradeCount>(), subjects = new List<SubjectCount>() };
+                                    itemConds.Add(condn);
+                                }
                             }
                         }
                     }

+ 1 - 1
TEAMModelOS/Controllers/Paper/PaperController.cs

@@ -157,7 +157,7 @@ namespace TEAMModelOS.Controllers
             
             if (scope.ToString().Equals("school"))
             {
-                sql.Append("select c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.sheetNo, c.tags from c");
+                sql.Append("select distinct c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.sheetNo, c.tags from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Paper>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {

+ 122 - 5
TEAMModelOS/Controllers/School/GroupListController.cs

@@ -65,10 +65,18 @@ namespace TEAMModelOS.Controllers
                     list.creatorId = userid;
                     list.school = school;
                     list.pk = "GroupList";
-                    list.code =  !list.code.StartsWith("GroupList-")?$"GroupList-{list.code}":list.code;
+                    if (list.scope.Equals("private"))
+                    {
+                        //私人名单
+                        list.code = "GroupList";
+                    }
+                    else {
+                        //学校自定义名单
+                        list.code = !list.code.StartsWith("GroupList-") ? $"GroupList-{list.code}" : list.code;
+                    }
                     switch (true)
                     {
-                        //普通学生名单(包含学校教学班名单,个人课程名单),其中学生成员账号类型可以是学校学生账号和醍摩豆ID,分区键为GroupList-hbcn  分区键为GroupList-tmdid
+                        //普通学生名单(包含学校教学班名单,个人课程名单),其中学生成员账号类型可以是学校学生账号和醍摩豆ID,分区键为GroupList-hbcn  分区键为GroupList
                         case bool when $"{_type}".Equals("student", StringComparison.OrdinalIgnoreCase):
                             list.type = "student";
                             list = await CheckListNo(list);
@@ -77,11 +85,11 @@ namespace TEAMModelOS.Controllers
                         case bool when $"{_type}".Equals("research", StringComparison.OrdinalIgnoreCase):
                             list.type = "research";
                             break;
-                        //个人好友名单,成员账号类型可以是学校学生账号和醍摩豆ID,分区键为GroupList-tmdid
+                        //个人好友名单,成员账号类型可以是学校学生账号和醍摩豆ID,分区键为GroupList
                         case bool when $"{_type}".Equals("friend", StringComparison.OrdinalIgnoreCase):
                             list.type = "friend";
                             break;
-                        //社交群组类型(包含学校交流群组,个人交流群组),成员账号类型可以是学校学生账号和醍摩豆ID,,分区键为GroupList-hbcn  分区键为GroupList-tmdid
+                        //社交群组类型(包含学校交流群组,个人交流群组),成员账号类型可以是学校学生账号和醍摩豆ID,,分区键为GroupList-hbcn  分区键为GroupList
                         case bool when $"{_type}".Equals("group", StringComparison.OrdinalIgnoreCase):
                             list.type = "group";
                             list = await CheckListNo(list);
@@ -96,7 +104,116 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
-
+        private async Task<GroupList> UpsertList(GroupList list) {
+            bool isnew = false;
+            var client = _azureCosmos.GetCosmosClient();
+            if (string.IsNullOrEmpty(list.id)) {
+                list.id = Guid.NewGuid().ToString();
+                isnew = true;
+            }
+            string tbname = list.scope.Equals("private") ? "Teacher" : "School";
+            await client.GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(list, new PartitionKey(list.code));
+            //学生名单,教研组会触发活动中间表刷新
+            if (list.type.Equals("student") || list.type.Equals("research")) {
+                GroupChange change = new GroupChange()
+                {
+                    type = list.type,
+                    listid = list.id,
+                    scope = list.scope,
+                    originCode = list.school,
+                    school = list.school,
+                    creatorId = list.creatorId
+                };
+                GroupList oldList = null;
+                if (!isnew)
+                {
+                    try {
+                        oldList = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<GroupList>(list.id, new PartitionKey(list.code));
+                    } catch (CosmosException) {
+                        oldList = null; 
+                    }
+                }
+                if (list.members.IsNotEmpty()&&(oldList == null || !oldList.members.IsNotEmpty()))
+                {
+                    //加入的
+                    var tmdids = list.members.FindAll(x => x.type == 1);
+                    if (tmdids.IsNotEmpty()) {
+                        if (list.type.Equals("research"))
+                        {
+                            change.tchjoin.AddRange(tmdids);
+                        }
+                        else {
+                            change.tmdjoin.AddRange(tmdids);
+                        }
+                    }
+                    var stuids = list.members.FindAll(x => x.type == 2);
+                    if (stuids.IsNotEmpty()) {
+                        change.stujoin.AddRange(stuids);
+                    }
+                }
+                else
+                {
+                    if (list.members.IsNotEmpty())
+                    {
+                        var tmdids = list.members.FindAll(x => x.type == 1);
+                        var stuids = list.members.FindAll(x => x.type == 2);
+                        var oldtmdids = oldList.members.FindAll(x => x.type == 1);
+                        var oldstuids = oldList.members.FindAll(x => x.type == 2);
+                        //取各自的差集
+                        var tmdidexp = tmdids.Select(x => x.id).Except(oldtmdids.Select(y => y.id)).ToList();
+                        var stuidexp = stuids.Select(x => x.id).Except(oldstuids.Select(y => y.id)).ToList();
+                        //在新的找到,代表加入的
+                        var jointmd = tmdids.Where(x => tmdidexp.Contains(x.id));
+                        //在旧的找到,代表离开的
+                        var leavetmd = oldtmdids.Where(x => tmdidexp.Contains(x.id));
+                        if (list.type.Equals("research"))
+                        {
+                            change.tchjoin.AddRange(jointmd);
+                            change.tchleave.AddRange(leavetmd);
+                        }
+                        else {
+                            change.tmdjoin.AddRange(jointmd);
+                            change.tmdleave.AddRange(leavetmd);
+                        }
+                        //在新的找到,代表加入的
+                        var joinstu = stuids.Where(x => stuidexp.Contains(x.id));
+                        //在旧的找到,代表离开的
+                        var leavestu = oldstuids.Where(x => stuidexp.Contains(x.id));
+                        change.stujoin.AddRange(joinstu); 
+                        change.stuleave.AddRange(leavestu);
+                    }
+                    else {
+                        //离开的
+                        var tmdids = oldList.members.FindAll(x => x.type == 1);
+                        if (tmdids.IsNotEmpty())
+                        {
+                            if (list.type.Equals("research"))
+                            {
+                                change.tchleave.AddRange(tmdids);
+                            }
+                            else
+                            {
+                                change.tmdleave.AddRange(tmdids);
+                            }
+                        }
+                        var stuids = oldList.members.FindAll(x => x.type == 2);
+                        if (stuids.IsNotEmpty())
+                        {
+                            change.stuleave.AddRange(stuids);
+                        }
+                    }
+                }
+                if (change.tmdjoin.Count != 0 || change.tmdleave.Count != 0 || change.stujoin.Count != 0 || change.stuleave.Count != 0
+                    || change.tchjoin.Count != 0 || change.tchleave.Count != 0)
+                {
+                    var messageChange = new ServiceBusMessage(change.ToJsonString());
+                    messageChange.ApplicationProperties.Add("name", "GroupChange");
+                    var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
+                    await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
+                }
+            }
+            return list;
+        }
         private async Task<GroupList> CheckListNo(GroupList list ) {
             try {
                 var client = _azureCosmos.GetCosmosClient();

+ 32 - 10
TEAMModelOS/Controllers/Third/ScTrainController.cs

@@ -20,6 +20,8 @@ using HTEXLib.COMM.Helpers;
 using TEAMModelOS.SDK;
 using System.IdentityModel.Tokens.Jwt;
 using TEAMModelOS.Services;
+using TEAMModelOS.SDK.Models.Service;
+using System.IO;
 
 namespace TEAMModelOS.Controllers
 {
@@ -42,9 +44,10 @@ namespace TEAMModelOS.Controllers
         private readonly AzureStorageFactory _azureStorage;
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly AzureRedisFactory _azureRedis;
+        private readonly AccountHttpService _accountHttpService;
         public IConfiguration _configuration { get; set; }
         public ScTrainController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
-          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration)
+          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, AccountHttpService accountHttpService)
         {
             _azureCosmos = azureCosmos;
             _snowflakeId = snowflakeId;
@@ -54,6 +57,7 @@ namespace TEAMModelOS.Controllers
             _serviceBus = serviceBus;
             _configuration = configuration;
             _azureRedis = azureRedis;
+            _accountHttpService = accountHttpService;
         }
         /// <summary>
         /// 
@@ -80,19 +84,19 @@ namespace TEAMModelOS.Controllers
                     {
                         long ssotime = long.Parse($"{_time}");
                         long nowtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
-                        if (nowtime - ssotime > 60 * 10)//10分钟有效期
+                        if (nowtime - ssotime > 60 * 10)//10分钟有效期
                         { 
-                          //  return Ok(new { status = 2, msg = "登录超时!" });
+                          //  return Ok(new { status = 2, msg = "登录超时!" });
                         }
                     }
                     else
                     {
-                        return Ok(new { status = 1, msg = "参数异常!" });
+                        return Ok(new { status = 1, msg = "参数异常!" });
                     }
                 }
                 else
                 {
-                    return Ok(new { status = 1, msg = "参数异常!" });
+                    return Ok(new { status = 1, msg = "参数异常!" });
                 }
                 Teacher teacher = null;
                 TeacherInfo teacherInfo = null;
@@ -107,11 +111,24 @@ namespace TEAMModelOS.Controllers
                         }
                         if (teacher == null)
                         {
-                            return Ok(new { status = 0, msg = "没有绑定!" });
+                            return Ok(new { status = 0, msg = "没有绑定!" });
                         }
                         else
                         {
                             teacherInfo = await TeacherService.GetTeacherInfo(_azureCosmos, teacher, null, null, null, _azureStorage, _option);
+                            var url = _configuration.GetValue<string>("HaBookAuth:Account");
+                            var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                            var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                            var location = _option.Location;
+                            (int code, string content) = await _accountHttpService.Implicit(clientID, clientSecret, location,$"{url}/oauth2/implicit",
+                                new Dictionary<string, string>()
+                                {
+                                { "grant_type", "implicit" },
+                                { "client_id",clientID },
+                                { "account",teacher.id },
+                                { "nonce",Guid.NewGuid().ToString()
+                                }
+                                });
                             return Ok(new
                             {
                                 location = _option.Location,
@@ -132,7 +149,7 @@ namespace TEAMModelOS.Controllers
                     case bool when $"{_opt}".Equals("BindTmdidTidInfo", StringComparison.OrdinalIgnoreCase):
                         if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
                         var jwt = new JwtSecurityToken(id_token.GetString());
-                        //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
+                        //TODO 姝ら�璀塈dToken鍏堢啊鍠��鏌ワ紝寰岄潰闇€鍚慍ore ID鏂癆PI锛岄�璀塗oken
                         if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
                         var id = jwt.Payload.Sub;
                         jwt.Payload.TryGetValue("name", out object name);
@@ -143,8 +160,13 @@ namespace TEAMModelOS.Controllers
                         {
                             teacherInfo.teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
                         }
-                        teacherInfo.teacher.binds.Add(new Teacher.ThirdBind { pxid = $"{_Pxid}", tid = $"{_tid}" ,webid=$"{_Webid}"});
+                        var bind=  teacherInfo.teacher.binds.FindAll(x => x.pxid.Equals($"{_Pxid}") && x.webid.Equals($"{_Webid}") && x.tid.Equals($"{_tid}"));
+                        if (bind.IsEmpty())
+                        {
+                            teacherInfo.teacher.binds.Add(new Teacher.ThirdBind { pxid = $"{_Pxid}", tid = $"{_tid}", webid = $"{_Webid}" });
+                        }
                         await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacherInfo.teacher, id, new PartitionKey("Base"));
+                      
                         return Ok(new
                         {
                             location = _option.Location,
@@ -164,10 +186,10 @@ namespace TEAMModelOS.Controllers
                 }
             } catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
-            return Ok(new { status = 1, msg = "参数异常!" });
+            return Ok(new { status = 1, msg = "参数异常!" });
         }
     }
 }

+ 1 - 0
TEAMModelOS/Startup.cs

@@ -106,6 +106,7 @@ namespace TEAMModelOS
             services.AddHttpClient();
             services.AddHttpClient<DingDing>();
             services.AddHttpClient<NotificationService>();
+            services.AddHttpClient<AccountHttpService>();
             services.AddMemoryCache();
             services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
             services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = false; });

+ 3 - 3
TEAMModelOS/TEAMModelOS.csproj

@@ -37,9 +37,9 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2110.21</Version>
-    <AssemblyVersion>5.2110.21.1</AssemblyVersion>
-    <FileVersion>5.2110.21.1</FileVersion>
+    <Version>5.2110.22</Version>
+    <AssemblyVersion>5.2110.22.1</AssemblyVersion>
+    <FileVersion>5.2110.22.1</FileVersion>
     <Description>TEAMModelOS(IES5)</Description>
     <PackageReleaseNotes>版本说明</PackageReleaseNotes>
   </PropertyGroup>

+ 2 - 0
TEAMModelOS/appsettings.Development.json

@@ -44,6 +44,8 @@
     "CoreId": {
       "userinfo": "https://api2.teammodel.cn/Oauth2/GetUserInfos"
     },
+    "Account": "https://account.teammodel.cn",
+    "CoreAPI": "https://api2.teammodel.cn",
     "CoreService": {
       "clientID": "c7317f88-7cea-4e48-ac57-a16071f7b884",
       "clientSecret": "kguxh:V.PLmxBdaI@jnrTrDSth]A3346",

+ 2 - 0
TEAMModelOS/appsettings.json

@@ -43,6 +43,8 @@
     "CoreId": {
       "userinfo": "https://api2.teammodel.cn/Oauth2/GetUserInfos"
     },
+    "Account": "https://account.teammodel.cn",
+    "CoreAPI": "https://api2.teammodel.cn",
     "CoreService": {
       "deviceinfo": "https://api2.teammodel.cn/oauth2/getdeviceinfos",
       "sendnotification": "https://api2.teammodel.net/service/sendnotification",