Browse Source

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

XW 11 months ago
parent
commit
57374314dc
60 changed files with 9942 additions and 331 deletions
  1. 0 7
      TEAMModelBI/Controllers/Census/SchoolController.cs
  2. 1 1
      TEAMModelBI/TEAMModelBI.csproj
  3. 0 10
      TEAMModelBI/Tool/CosmosBank/StatsWay.cs
  4. 1 1
      TEAMModelContest/Contest.Server/Contest.Server.csproj
  5. 264 0
      TEAMModelOS.Function/.gitignore
  6. 863 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerArt.cs
  7. 457 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerCorrect.cs
  8. 2225 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerExam.cs
  9. 176 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerExamImport.cs
  10. 224 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerExamLite.cs
  11. 412 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerHomework.cs
  12. 240 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerQuotaImport.cs
  13. 280 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerStudy.cs
  14. 530 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerSurvey.cs
  15. 422 0
      TEAMModelOS.Function/CosmosDBTriggers/TriggerVote.cs
  16. 51 0
      TEAMModelOS.Function/DI/BackgroundWorkerQueue.cs
  17. 135 0
      TEAMModelOS.Function/IESCosmosDBTrigger.cs
  18. 986 0
      TEAMModelOS.Function/IESHttpTrigger.cs
  19. 2063 0
      TEAMModelOS.Function/IESServiceBusTrigger.cs
  20. 105 0
      TEAMModelOS.Function/IESTimerTrigger.cs
  21. 31 0
      TEAMModelOS.Function/Lang/en-us.json
  22. 31 0
      TEAMModelOS.Function/Lang/zh-cn.json
  23. 31 0
      TEAMModelOS.Function/Lang/zh-tw.json
  24. 43 0
      TEAMModelOS.Function/Program.cs
  25. 174 0
      TEAMModelOS.Function/Properties/ServiceDependencies/teammodelosfunction-test - Zip Deploy/profile.arm.json
  26. 9 0
      TEAMModelOS.Function/Properties/launchSettings.json
  27. 12 0
      TEAMModelOS.Function/Properties/serviceDependencies.json
  28. 11 0
      TEAMModelOS.Function/Properties/serviceDependencies.local.json
  29. 14 0
      TEAMModelOS.Function/Properties/serviceDependencies.teammodelosfunction-test - Zip Deploy.json
  30. 58 0
      TEAMModelOS.Function/TEAMModelOS.Function.csproj
  31. 12 0
      TEAMModelOS.Function/host.json
  32. 11 0
      TEAMModelOS.Function/readme.md
  33. 0 9
      TEAMModelOS.FunctionV4/Properties/serviceDependencies.TEAMModelFunction - Zip Deploy.json
  34. 0 1
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  35. 1 2
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  36. 2 0
      TEAMModelOS.FunctionV4/local.settings.json
  37. 0 2
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosExtensions.cs
  38. 9 7
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactory.cs
  39. 0 2
      TEAMModelOS.SDK/DI/AzureServiceBus/AzureServiceBusExtensions.cs
  40. 0 8
      TEAMModelOS.SDK/DI/AzureServiceBus/AzureServiceBusFactory.cs
  41. 0 1
      TEAMModelOS.SDK/DI/HttpTrigger/WebHookHttpTrigger.cs
  42. 9 3
      TEAMModelOS.SDK/DI/Multiple/MultipleAzureCosmosFactoryExtensions.cs
  43. 0 46
      TEAMModelOS.SDK/Helper/Common/JsonHelper/JsonPatchHelper.cs
  44. 0 3
      TEAMModelOS.SDK/Helper/Common/StringHelper/PingYinHelper.cs
  45. 1 2
      TEAMModelOS.SDK/Models/Cosmos/Common/StudentScoreRecord.cs
  46. 1 5
      TEAMModelOS.SDK/Models/Service/BI/BICommonWay.cs
  47. 0 2
      TEAMModelOS.SDK/Models/Service/BI/BIStats.cs
  48. 0 13
      TEAMModelOS.SDK/Models/Service/StudentService.cs
  49. 0 14
      TEAMModelOS.SDK/Models/Service/SystemService.cs
  50. 24 28
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  51. 2 2
      TEAMModelOS.TEST/TEAMModelOS.TEST.csproj
  52. 12 6
      TEAMModelOS.sln
  53. 1 40
      TEAMModelOS/Controllers/Both/ScoreCalcController.cs
  54. 0 1
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  55. 1 22
      TEAMModelOS/Controllers/Common/ArtController.cs
  56. 1 10
      TEAMModelOS/Controllers/School/ImportExamController.cs
  57. 0 31
      TEAMModelOS/Controllers/System/BillController.cs
  58. 0 36
      TEAMModelOS/Controllers/XTest/TestController.cs
  59. 1 11
      TEAMModelOS/Filter/RequestAuditFilter.cs
  60. 5 5
      TEAMModelOS/TEAMModelOS.csproj

+ 0 - 7
TEAMModelBI/Controllers/Census/SchoolController.cs

@@ -1,17 +1,11 @@
 using Azure.Core;
 using Azure.Cosmos;
-using Azure.Storage.Blobs;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Spreadsheet;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
-using NUnit.Framework.Constraints;
 using Pipelines.Sockets.Unofficial.Arenas;
 using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Text.Json;
@@ -20,7 +14,6 @@ using TEAMModelBI.Models;
 using TEAMModelBI.Tool;
 using TEAMModelBI.Tool.CosmosBank;
 using TEAMModelOS.Models;
-using TEAMModelOS.SDK.Context.BI;
 using TEAMModelOS.SDK.Context.Constant;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;

+ 1 - 1
TEAMModelBI/TEAMModelBI.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 	<PropertyGroup>
-		<TargetFramework>net6.0</TargetFramework>
+		<TargetFramework>net8.0</TargetFramework>
 		<RootNamespace>TEAMModelBI</RootNamespace>
 	</PropertyGroup>
 

+ 0 - 10
TEAMModelBI/Tool/CosmosBank/StatsWay.cs

@@ -1,23 +1,13 @@
 using Azure.Cosmos;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Math;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using Microsoft.OData.Edm;
-using NUnit.Framework.Constraints;
-using OpenXmlPowerTools;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text.Json;
 using System.Threading.Tasks;
-using TEAMModelBI.Models;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.BI;
-using TEAMModelOS.SDK.Models.Cosmos.BI.BITable;
 using TEAMModelOS.SDK.Models.Service.BI;
-using TEAMModelOS.SDK.Models.Service.BIStatsWay;
-using static TEAMModelBI.Tool.CosmosBank.StatsWay;
 
 namespace TEAMModelBI.Tool.CosmosBank
 {

+ 1 - 1
TEAMModelContest/Contest.Server/Contest.Server.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
     <SpaRoot>..\contest.client</SpaRoot>

+ 264 - 0
TEAMModelOS.Function/.gitignore

@@ -0,0 +1,264 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# Azure Functions localsettings file
+local.settings.json
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc

+ 863 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerArt.cs

@@ -0,0 +1,863 @@
+using Azure.Messaging.ServiceBus;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK;
+using Azure.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+
+using TEAMModelOS.Function;
+
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public class TriggerArt
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+            CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration, HttpTrigger _httpTrigger)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404))
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                    //删除blob 相关资料
+                    await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, tdata.school, new List<string> { $"art/{tdata.id}" });
+                   // await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                        }
+                        catch (Exception)
+                        {
+                            continue;
+                        }
+                    }
+                    return;
+                }
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+                ArtEvaluation art = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ArtEvaluation>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+
+                if (art != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", art.code, "-", art.progress);
+                    List<ChangeRecord> voteRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (art.progress)
+                    {
+                        case "pending":
+                            var messageVote = new ServiceBusMessage(new { tdata.id, progress = "going", code = tdata.code }.ToJsonString());
+                            messageVote.ApplicationProperties.Add("name", "Art");
+                            if (voteRecords.Count > 0)
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), voteRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                voteRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                            }
+                            else
+                            {
+
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new ChangeRecord
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageVote.MessageId
+                                };
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":
+                            /*
+                             if (art.classes.Count > 0)
+                            {
+
+                               List<string> classes = ExamService.getClasses(art.classes, art.stuLists);
+                               (List<RMember> tmdIds, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, art.school, null);
+                               var addStudentsCls = tmdIds.FindAll(x => x.type == 2);
+                               var addTmdidsCls = tmdIds.FindAll(x => x.type == 1);
+                               List<string> tmds = new List<string>();
+                               if (addTmdidsCls.IsNotEmpty())
+                               {
+                                   tmds.AddRange(addTmdidsCls.Select(x => x.id).ToList());
+                               }
+                               List<StuActivity> stuActivities = new List<StuActivity>();
+                               List<StuActivity> tmdActivities = new List<StuActivity>();
+                               List<StuActivity> tchActivities = new List<StuActivity>();
+                               List<string> sub = new();
+                               if (art.subjects.Count > 0)
+                               {
+                                   foreach (var course in art.subjects)
+                                   {
+                                       sub.Add(course.id);
+                                   }
+                               }
+                               if (tmds.IsNotEmpty())
+                               {
+                                   tmds.ForEach(x =>
+                                   {
+                                       HashSet<string> classIds = new HashSet<string>();
+                                       classLists.ForEach(z =>
+                                       {
+                                           z.members.ForEach(y =>
+                                           {
+                                               if (y.id.Equals(x) && y.type == 1)
+                                               {
+                                                   classIds.Add(z.id);
+                                               }
+                                           });
+                                       });
+                                       tmdActivities.Add(new StuActivity
+                                       {
+                                           pk = "Activity",
+                                           id = art.id,
+                                           code = $"Activity-{x}",
+                                           type = "Art",
+                                           name = art.name,
+                                           startTime = art.startTime,
+                                           endTime = art.endTime,
+                                           scode = art.code,
+                                           scope = art.scope,
+                                           school = art.school,
+                                           creatorId = art.creatorId,
+                                           subjects = sub,
+                                           blob = null,
+                                           owner = art.owner,
+                                           createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                           ext = new Dictionary<string, JsonElement>() { { "subjects", art.subjects.ToJsonString().ToObject<JsonElement>() } },
+                                           taskStatus = -1,
+                                           classIds = classIds.ToList()
+                                       });
+                                   });
+                               }
+                               if (addStudentsCls.IsNotEmpty())
+                               {
+                                   addStudentsCls.ForEach(x =>
+                                   {
+                                       HashSet<string> classIds = new HashSet<string>();
+                                       classLists.ForEach(z =>
+                                       {
+                                           z.members.ForEach(y =>
+                                           {
+                                               if (y.id.Equals(x.id) && y.code.Equals(art.school) && y.type == 2)
+                                               {
+                                                   classIds.Add(z.id);
+                                               }
+                                           });
+                                       });
+                                       stuActivities.Add(new StuActivity
+                                       {
+                                           pk = "Activity",
+                                           id = art.id,
+                                           code = $"Activity-{x.code.Replace("Base-", "")}-{x.id}",
+                                           type = "Art",
+                                           name = art.name,
+                                           startTime = art.startTime,
+                                           endTime = art.endTime,
+                                           scode = art.code,
+                                           scope = art.scope,
+                                           school = art.school,
+                                           creatorId = art.creatorId,
+                                           subjects = sub,
+                                           blob = null,
+                                           owner = art.owner,
+                                           createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                           ext = new Dictionary<string, JsonElement>() { { "subjects", art.subjects.ToJsonString().ToObject<JsonElement>() } },
+                                           taskStatus = -1,
+                                           classIds = classIds.ToList()
+                                       });
+                                   });
+                               }
+                               await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+
+
+                           }*/
+                            try
+                            {
+                                var messageVoteEnd = new ServiceBusMessage(new { tdata.id, progress = "finish", tdata.code }.ToJsonString());
+                                messageVoteEnd.ApplicationProperties.Add("name", "Art");
+                                if (voteRecords.Count > 0)
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    try
+                                    {
+                                        await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), voteRecords[0].sequenceNumber);
+                                    }
+                                    catch (Exception)
+                                    {
+                                    }
+                                    voteRecords[0].sequenceNumber = end;
+                                    await table.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                                }
+                                else
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    ChangeRecord changeRecord = new()
+                                    {
+                                        RowKey = tdata.id,
+                                        PartitionKey = PartitionKey,
+                                        sequenceNumber = end,
+                                        msgId = messageVoteEnd.MessageId
+                                    };
+                                    await table.Save<ChangeRecord>(changeRecord);
+                                }
+
+                            }
+                            catch (Exception e)
+                            {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-艺术评测going{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                            }
+                            finally {
+                                string pkey = string.Format("{0}{1}{2}", art.code, "-", "pending");
+                                await table.DeleteSingle<ChangeRecord>(pkey, tdata.id);
+                            }
+                            
+
+                            break;
+                        case "finish":
+
+                            //判定是否是区级创建的活动内容
+                            /* if (art.lost.Count == 0 && art.pass == 0)
+                             {
+                                 if (art.owner.Equals("area") && string.IsNullOrEmpty(art.pId))
+                                 {
+                                     *//* List<(string id, string code, List<Tasks> settings)> artSchools = new();
+                                      string ql = $"select c.id,c.school,c.settings,c.classes from c where  c.pk = 'Art' and c.pId = '{art.id}'";
+                                      await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: ql))
+                                      {
+                                          using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                                          if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                          {
+                                              var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
+                                              while (accounts.MoveNext())
+                                              {
+                                                  JsonElement account = accounts.Current;
+                                                  List<Tasks> settings = account.GetProperty("settings").ToObject<List<Tasks>>();
+                                                  artSchools.Add((account.GetProperty("id").GetString(), account.GetProperty("school").GetString(), settings));
+
+                                              }
+                                          }
+                                      }
+                                      foreach (var (id, code, settings) in artSchools)
+                                      {
+                                          List<(string eId, string sId)> ids = new();
+                                          var examIds = settings.SelectMany(s => s.task).Where(a => a.type == 1).Select(z => new { z.acId, z.subject }).ToList();
+                                          examIds.ForEach(x =>
+                                          {
+                                              ids.Add((x.acId, x.subject));
+                                          });
+                                          List<(string code, string sub, List<string> stu)> stuInfo = await getLostAsync(ids, client, code);
+                                          List<string> stus = new();
+                                          foreach (var lost in stuInfo)
+                                          {
+                                              if (stus.Count == 0)
+                                              {
+                                                  stus = stus.Union(lost.stu).ToList();
+                                              }
+                                              else {
+                                                  stus = stus.Intersect(lost.stu).ToList();
+                                              }                                          
+                                              LostStudent lostStudent = new()
+                                              {
+                                                  code = lost.code,
+                                                  subject = lost.sub,
+                                                  stu = lost.stu.Count
+                                              };
+                                              art.lost.Add(lostStudent);
+                                          }
+                                          art.miss.Add(stus.Count);
+
+                                      }
+                                      art.pass = 1;
+                                      await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync<ArtEvaluation>(art, art.id, new PartitionKey(art.code));*//*
+                                 }
+                                 else
+                                 {
+                                     //获取当前艺术评价相关评测ID目前暂时排除区级发布的评测信息
+
+                                     *//*List<(string eId, string sId)> ids = new();
+                                     var examId = art.settings.SelectMany(x => x.task).Where(c => c.type == 1).Select(z => new { z.acId, z.subject }).ToList();
+                                     examId.ForEach(x =>
+                                     {
+                                         ids.Add((x.acId, x.subject));
+                                     });
+                                     List<(string code, string sub, List<string> stu)> stuInfo = await getLostAsync(ids, client, art.school);
+                                     List<string> stus = new();
+                                     foreach (var (code, sub, stu) in stuInfo)
+                                     {
+                                         if (stus.Count == 0)
+                                         {
+                                             stus = stus.Union(stu).ToList();
+                                         }
+                                         else
+                                         {
+                                             stus = stus.Intersect(stu).ToList();
+                                         }
+                                         LostStudent lostStudent = new()
+                                         {
+                                             code = code,
+                                             subject = sub,
+                                             stu = stu.Count
+                                         };
+                                         art.lost.Add(lostStudent);
+                                     }
+                                     art.miss.Add(stus.Count);
+                                     //art.miss = stus.Count;
+                                     art.pass = 1;
+                                     await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync<ArtEvaluation>(art, art.id, new PartitionKey(art.code));*//*
+                                 }
+                             }*/
+                            //根据学校编码去获取区级ID
+                            try {
+                                School scInfo = new();
+                                //School scInfo = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{art.school}", partitionKey: new Azure.Cosmos.PartitionKey("Base"));
+                                var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{art.school}", partitionKey: new Azure.Cosmos.PartitionKey("Base"));
+                                if (response.Status == 200)
+                                {
+                                    using var cJson = await JsonDocument.ParseAsync(response.ContentStream);
+                                    scInfo = cJson.ToObject<School>();
+                                }
+                                ArtSetting setting = new();
+                                try
+                                {
+                                    var artResponse = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{scInfo.areaId}", partitionKey: new Azure.Cosmos.PartitionKey("ArtSetting"));
+                                    if (response.Status == 200)
+                                    {
+                                        using var json = await JsonDocument.ParseAsync(artResponse.ContentStream);
+                                        setting = json.ToObject<ArtSetting>();
+                                    }
+                                }
+                                catch (Exception e)
+                                {
+
+                                }
+                                if (art.classes.Any())
+                                {
+                                    //获取该艺术评测的评测Id
+                                    List<(string id, string subjectId)> ids = new();
+                                    List<ExamClassResult> examClassResults = new();
+                                    var examId = art.settings.SelectMany(x => x.task).Where(c => c.type == 1).Select(z => new { z.acId, z.subject }).ToList();
+                                    examId.ForEach(x =>
+                                    {
+                                        ids.Add((x.acId, x.subject));
+                                    });
+                                    List<string> results = new();
+                                    results = ids.Select(x => x.id).ToList();
+                                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c where c.examId in ({string.Join(",", results.Select(x => $"'{x}'"))})", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{art.school}") }))
+                                    {
+                                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                        {
+                                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                            {
+                                                examClassResults.Add(obj.ToObject<ExamClassResult>());
+                                            }
+                                        }
+                                    }
+                                    //获取该艺术评测下面的评测活动
+                                    List<ExamInfo> exams = new();
+                                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryIterator<ExamInfo>(queryText: $"select value(c) from c where c.pk = 'Exam' and c.id in ({string.Join(",", results.Select(o => $"'{o}'"))})"))
+                                    {
+                                        exams.Add(item);
+                                    }
+                                    var sta = examClassResults.SelectMany(x => x.status).ToList();
+                                    var ansCount = sta.Where(x => x == 0).ToList();
+                                    var persent = ansCount.Count * 1.0 / sta.Count * 100;
+                                    var period = scInfo.period.Where(x => x.id.Equals(art.period.id))?.FirstOrDefault();
+
+                                    List<StudentArtResult> studentArtResults = new();
+                                    string sql = $"SELECT value c FROM c   where  c.pk='ArtResult' ";
+                                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Student)
+                                        .GetItemQueryIterator<StudentArtResult>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"ArtResult-{art.id}") }))
+                                    {
+                                        studentArtResults.Add(item);
+                                    }
+                                    List<Task<ItemResponse<StudentArtResult>>> tasks = new List<Task<ItemResponse<StudentArtResult>>>();
+                                    //新增数据推送 obj => Portrait
+                                    Portrait portrait = new()
+                                    {
+                                        schoolCode = art.school,
+                                        periodId = art.period?.id,
+                                        subjectId = "subject_art"
+                                    };
+                                    //var period = scInfo.period.Where(x => x.id.Equals(art.period.id)).FirstOrDefault();
+                                    List<(string ptype, string subId, List<(string name, List<string> kno)> knos)> knoledge = new();
+                                    //List<string> subs = new List<string> { "subject_painting", "subject_music" };
+                                    if (exams.Count == 0) return;
+                                    foreach (var ss in art.subjects)
+                                    {
+                                        if (string.IsNullOrEmpty(exams[0].papers[0].periodId))
+                                        {
+
+                                            knoledge.Add(await getKnowledge("university", "hbcn", client, ss.id, "be32942d-97a9-52ba-45d6-2e5b722583f5"));
+                                        }
+                                        else
+                                        {
+                                            knoledge.Add(await getKnowledge(art.periodType, exams.Where(c => c.subjects[0].id.Equals(ss.id))?.FirstOrDefault().papers[0].code, client, ss.id, exams.Where(c => c.subjects[0].id.Equals(ss.id))?.FirstOrDefault().papers[0].periodId));
+                                        }
+
+                                    }
+
+                                    List<(string name, double score, double aver, string subject)> blockScore = new();
+                                    List<(List<(string name, double score, double point, string subject)> studentScore, string stuId)> studentScores = new();
+                                    foreach (var exam in exams)
+                                    {
+                                        HashSet<string> knowledge = new HashSet<string>();
+                                        List<double> point = new List<double>();
+                                        List<List<double>> result = new List<List<double>>();
+                                        List<ClassRange> classes = new List<ClassRange>();
+                                        //求单个知识点所占分数
+                                        List<string> per = new List<string>();
+
+                                        if (exam.papers[0].knowledge != null && exam.papers[0].knowledge.Count > 0)
+                                        {
+                                            exam.papers[0].knowledge.ForEach(k =>
+                                            {
+                                                k.ForEach(e =>
+                                                {
+                                                    knowledge.Add(e);
+                                                });
+                                            });
+                                        }
+                                        point = exam.papers[0].point;
+
+                                        List<string> knowledgeName = new List<string>();
+                                        foreach (string cla in knowledge)
+                                        {
+                                            knowledgeName.Add(cla);
+                                        }
+                                        for (int k = 0; k < knowledgeName.Count; k++)
+                                        {
+                                            if (null == knowledgeName[k])
+                                            {
+                                                knowledgeName.Remove(knowledgeName[k]);
+                                            }
+                                        }
+                                        List<double> Score = new List<double>();
+                                        //List<(string name, double score, string subject)> pointScore = new();
+                                        List<string> stus = examClassResults.Where(c => c.examId.Equals(exam.id)).SelectMany(z => z.studentIds).ToList();
+                                        List<List<double>> stuScores = examClassResults.Where(c => c.examId.Equals(exam.id)).SelectMany(z => z.studentScores).ToList();
+                                        foreach (string id in stus)
+                                        {
+                                            //double scores = 0;
+                                            List<(string name, double score, double point, string subject)> studentScore = new();
+                                            for (int k = 0; k < knowledgeName.Count; k++)
+                                            {
+                                                int n = 0;
+                                                double OnePoint = 0;
+                                                double scores = 0;
+                                                exam.papers[0].knowledge.ForEach(kno =>
+                                                {
+                                                    if (kno.Contains(knowledgeName[k]))
+                                                    {
+                                                        var itemPersent = kno.Count > 0 ? 1 / Convert.ToDouble(kno.Count) : 0;
+                                                        OnePoint += point[n] * itemPersent;
+                                                        int index = stus.IndexOf(id);
+                                                        if (stuScores[index][n] > 0)
+                                                        {
+                                                            scores += stuScores[index][n] * itemPersent;
+                                                        }
+                                                    }
+                                                    n++;
+                                                });
+                                                studentScore.Add((knowledgeName[k], Math.Round(scores, 2), OnePoint, exam.subjects[0].id));
+                                            }
+                                            studentScores.Add((studentScore, id));
+                                        }
+                                        for (int k = 0; k < knowledgeName.Count; k++)
+                                        {
+                                            double OnePoint = 0;
+                                            int n = 0;
+                                            double scores = 0;
+                                            exam.papers[0].knowledge.ForEach(kno =>
+                                            {
+                                                if (kno.Contains(knowledgeName[k]))
+                                                {
+                                                    var itemPersent = kno.Count > 0 ? 1 / Convert.ToDouble(kno.Count) : 0;
+                                                    OnePoint += point[n] * itemPersent;
+                                                    foreach (string id in stus)
+                                                    {
+                                                        int index = stus.IndexOf(id);
+                                                        if (stuScores[index][n] > 0)
+                                                        {
+                                                            scores += stuScores[index][n] * itemPersent;
+                                                        }
+                                                    }
+                                                }
+                                                n++;
+                                            });
+                                            double sc = stus.Count > 0 ? Math.Round(scores * 1.0 / stus.Count, 2) : 0;
+                                            blockScore.Add((knowledgeName[k], OnePoint, sc, exam.subjects[0].id));
+                                            //blockScore.AddRange(pointScore);
+                                        }
+                                    }
+                                    var bls = blockScore.GroupBy(x => x.subject).Select(v => new
+                                    {
+                                        subjectId = v.Key,
+                                        knoScore = v.ToList().GroupBy(k => k.name).Select(z => new
+                                        {
+                                            knoName = z.Key,
+                                            score = z.ToList().Sum(j => j.score),
+                                            aver = z.ToList().Sum(j => j.aver)
+                                        })
+                                    });
+                                    //List<(string subId,List<(string name, List<string> kno)>)> subjectKnow = knoledge.Select(x => new { x.subId, x.knos }).ToList();
+                                    //var subjectKnow;
+                                    var subjectKnow = string.IsNullOrEmpty(exams[0].papers[0].periodId) ? knoledge.Select(x => new { x.subId, x.knos }).ToList() : knoledge.Where(c => c.ptype.Equals(art.periodType)).Select(x => new { x.subId, x.knos }).ToList();
+                                    /*if (string.IsNullOrEmpty(exams[0].papers[0].periodId))
+                                    {
+                                        var subjectKnow = knoledge.Select(x => new { x.subId, x.knos }).ToList();
+                                    }
+                                    else {
+                                        var subjectKnow = knoledge.Where(c => c.ptype.Equals(art.periodType)).Select(x => new { x.subId, x.knos }).ToList();
+                                    }*/
+                                    List<(string subjectId, List<(string name, double score, double persent, double aver, List<string> dim)> bks)> bs = new();
+                                    List<(string subjectId, List<(string stuId, List<(string name, double score, double point, List<string> dim)> values)> stuBks)> sbs = new();
+                                    List<(string name, double score, double av, string sId)> stuBlockScore = new();
+                                    foreach (var bb in subjectKnow)
+                                    {
+                                        var kno1 = bls.Where(c => c.subjectId.Equals(bb.subId)).SelectMany(x => x.knoScore).ToList();
+                                        var stuInfo = studentScores.Select(c => new
+                                        {
+                                            c.stuId,
+                                            subInfo = c.studentScore.Where(x => x.subject.Equals(bb.subId)).ToList()
+                                        }).Where(z => z.subInfo.Count > 0).ToList();
+                                        //var kno2 = dimensions.Where(c => c.subjectBind.Equals(bb.subId)).Select(x => new { x.dimension,x.blocks}).ToList();
+                                        List<(string name, double score, double aver)> blockScores = new();
+                                        foreach (var k2 in bb.knos)
+                                        {
+                                            double bsc = 0;
+                                            double avs = 0;
+                                            foreach (var k3 in kno1)
+                                            {
+                                                if (null != k2.kno && k2.kno.Contains(k3.knoName))
+                                                {
+                                                    bsc += k3.score;
+                                                    avs += k3.aver;
+                                                }
+                                            }
+                                            foreach (var stu in stuInfo)
+                                            {
+                                                double realScore = 0;
+                                                double realPoint = 0;
+                                                foreach (var subScore in stu.subInfo)
+                                                {
+                                                    if (null != k2.kno && k2.kno.Contains(subScore.name))
+                                                    {
+                                                        realScore = subScore.score;
+                                                        realPoint = subScore.point;
+                                                    }
+                                                }
+                                                stuBlockScore.Add((k2.name, realScore, realPoint, stu.stuId));
+                                            }
+                                            blockScores.Add((k2.name, bsc, avs));
+                                        }
+
+
+                                        var stuBlock = stuBlockScore.GroupBy(x => (x.sId)).Select(c => new
+                                        {
+                                            stuId = c.Key,
+                                            block = c.ToList().GroupBy(b => b.name).Select(q => new
+                                            {
+                                                name = q.Key,
+                                                score = q.ToList().Sum(q => q.score),
+                                                point = q.ToList().Sum(q => q.av),
+                                                dimension = setting.dimensions.Where(s => s.blocks.Contains(q.Key)).Select(x => x.dimension).ToList()
+                                            })
+                                        }).ToList();
+                                        var blk = blockScores.Select(x => new
+                                        {
+                                            x.name,
+                                            x.score,
+                                            persent = Math.Round(x.aver > 0 ? x.aver / x.score : 0, 2),
+                                            x.aver,
+                                            dimension = setting.dimensions.Where(s => s.blocks.Contains(x.name)).Select(x => x.dimension)
+                                        });
+                                        List<(string name, double score, double persent, double aver, List<string> dim)> bks = new();
+                                        List<(string stuId, List<(string name, double score, double point, List<string> dim)> values)> stuBks = new();
+                                        foreach (var bk in blk)
+                                        {
+                                            bks.Add((bk.name, bk.score, bk.persent, bk.aver, bk.dimension.ToList()));
+                                        }
+                                        foreach (var stuBlk in stuBlock)
+                                        {
+                                            List<(string name, double score, double point, List<string> dim)> values = new();
+                                            foreach (var lk in stuBlk.block)
+                                            {
+                                                values.Add((lk.name, lk.score, lk.point, lk.dimension));
+                                            }
+                                            stuBks.Add((stuBlk.stuId, values));
+                                        }
+                                        bs.Add((bb.subId, bks));
+                                        sbs.Add((bb.subId, stuBks));
+                                    }
+
+                                    var blocks = bs.Select(x => new
+                                    {
+                                        x.subjectId,
+                                        dim = x.bks.Select(z => new
+                                        {
+                                            z.name,
+                                            z.score,
+                                            z.persent,
+                                            z.aver,
+                                            z.dim
+                                        })
+                                    });
+                                    var stuBlocks = sbs.Select(x => new {
+                                        x.subjectId,
+                                        dim = x.stuBks.Select(z => new {
+                                            z.stuId,
+                                            blk = z.values.Select(c => new {
+                                                c.name,
+                                                c.score,
+                                                c.point,
+                                                c.dim
+                                            })
+                                        })
+                                    });
+                                    //获取维度得分率
+                                    var dim = setting.dimensions.GroupBy(a => a.subjectBind).Select(x => new
+                                    {
+                                        x.Key,
+                                        dim = x.ToList().Select(c => new
+                                        {
+                                            c.dimension,
+                                            persent = Math.Round(c.blocks.Select(z => new
+                                            {
+                                                persent = Math.Round(bs.Where(z => z.subjectId.Equals(x.Key)).SelectMany(k => k.bks).Where(q => q.name.Equals(z)).Sum(h => h.score) > 0 ?
+                                                bs.Where(z => z.subjectId.Equals(x.Key)).SelectMany(k => k.bks).Where(q => q.name.Equals(z)).Sum(h => h.aver) /
+                                                bs.Where(z => z.subjectId.Equals(x.Key)).SelectMany(k => k.bks).Where(q => q.name.Equals(z)).Sum(h => h.score) : 0, 2)
+                                            }).Sum(o => o.persent) / c.blocks.Count, 2)
+                                        })
+                                    });
+
+                                    //获取学期信息
+
+                                    var (currSemester, studyYear, currSemesterDate, date, nextSemester) = SchoolService.GetSemester(period, art.startTime);
+                                    //总分的占比情况
+
+                                    foreach (var rs in studentArtResults)
+                                    {
+                                        foreach (var res in rs.results)
+                                        {
+                                            if (res.quotaId.Equals("quota_21") && res.score > -1 && res.score < 95)
+                                            {
+                                                /*res.score *= 1.5;
+                                                if (res.score >= 95) {
+                                                    res.score = new Random().Next(90, 99);
+                                                }*/
+                                                //res.score = Math.Round(res.score);
+                                            }
+                                        }
+
+                                        //if (rs.totalScore == 0)
+                                        //{
+                                        foreach (var sc in rs.subjectScores)
+                                        {
+                                            //if (sc.score == 0)
+                                            //{
+                                            /* var subjectMore = rs.results.GroupBy(x => x.subjectId).Select(c => new { subject = c.Key, list = c.ToList().Select(m => new { m.quotaId,m.score}) });
+                                             var totalScore = subjectMore.Select(x => new { 
+                                                 x.subject,
+                                                 quotaScroe =  x.list.Select(c => new {
+                                                     c.quotaId,
+                                                     c.score
+                                                 }),
+                                             });*/
+                                            var quotaPercent = setting.quotas.Select(x => new
+                                            {
+                                                x.id,
+                                                x.percent,
+                                                score = x.children.Select(c => new
+                                                {
+                                                    real = rs.results.Where(r => r.quotaId.Equals(c.id) && r.subjectId.Equals(sc.subjectId) && r.score > -1).FirstOrDefault()?.score * c.percent * 0.01,
+                                                    score = c.children.Select(s => new
+                                                    {
+                                                        real = rs.results.Where(r => r.quotaId.Equals(s.id) && r.subjectId.Equals(sc.subjectId) && r.score > -1).FirstOrDefault()?.score * s.percent * 0.01
+                                                    }).Sum(n => n.real) * c.percent * 0.01
+                                                }).Sum(n => n.real + n.score) * x.percent * 0.01
+                                            });
+                                            double realScore = Math.Round((double)quotaPercent.Sum(c => c.score), 2);
+                                            sc.score = realScore;
+                                            //}
+                                        }
+                                        rs.totalScore = Math.Round(rs.subjectScores.Where(m => m.score >= 0).Sum(z => z.score), 2);
+                                        tasks.Add(client.GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync(rs, rs.id, new PartitionKey(rs.code)));
+
+                                        PortraitStudent student = new()
+                                        {
+                                            studentId = rs.studentId,
+                                            name = rs.studentName,
+                                            classId = rs.classIds[0]
+                                        };
+                                        SemesterData semesterData = new()
+                                        {
+                                            examName = art.name,
+                                            examId = art.id,
+                                            examDate = art.startTime,
+                                            examType = "",
+                                            year = studyYear,
+                                            semesterId = currSemester.id,
+                                            totalScore = 200,
+                                            sumScore = rs.totalScore,
+                                            excellenceRate = 0,
+                                            passRate = 0,
+                                        };
+                                        int index = 0;
+                                        foreach (var sj in art.subjects)
+                                        {
+                                            ItemScore item = new()
+                                            {
+                                                name = sj.name,
+                                                score = rs.subjectScores.Where(x => x.subjectId.Equals(sj.id)).FirstOrDefault().score,
+                                                time = art.startTime,
+                                                totalScore = 100,
+                                                id = sj.id,
+                                                type = sj.id,
+                                                block = stuBlocks.Where(c => c.subjectId.Equals(sj.id)).SelectMany(x => x.dim).Where(z => z.stuId.Equals(rs.studentId))?.FirstOrDefault().blk,
+                                                kno = studentScores.Where(c => c.stuId.Equals(rs.studentId)).SelectMany(c => c.studentScore).Where(
+                                                    p => p.subject.Equals(sj.id)).Select(z => new
+                                                    {
+                                                        z.name,
+                                                        z.score,
+                                                        z.point,
+                                                        block = subjectKnow.Where(v => v.subId.Equals(sj.id)).SelectMany(k => k.knos).Where(c => null != c.kno && c.kno.Contains(z.name)).Select(x => x.name)
+                                                    }),
+                                                dim = dim.Where(c => c.Key.Equals(sj.id))?.FirstOrDefault().dim
+                                            };
+                                            index++;
+                                            semesterData.itemScore.Add(item);
+                                        }
+                                        student.semesterData.Add(semesterData);
+
+                                        portrait.students.Add(student);
+                                        //}
+                                    }
+                                    if (tasks.Count > 0)
+                                    {
+                                        await Task.WhenAll(tasks);
+                                    }
+                                    //获取学生信息
+                                    //(List<RMember> rmembers, List<RGroupList> groups) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, art.classes, art.school);
+                                    /* foreach (var member in studentArtResults)
+                                     {                                
+
+                                     }*/
+                                    if (null != period && persent >= 60)
+                                    {
+                                        string location = $"{Environment.GetEnvironmentVariable("Option:Location")}";
+                                        var responseData = await _httpTrigger.RequestHttpTrigger(portrait, location, "upsert-student-portrait");
+                                    }
+                                }
+                            } catch (Exception e) {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-艺术评测finish{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                            }
+                            finally
+                            {
+                                string pk = string.Format("{0}{1}{2}", art.code, "-", "going");
+                                await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            }
+                            
+
+                            break;
+                    }
+                }
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}艺术评价异常{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+            }
+
+        }
+
+        private static async Task<(string key, string subId, List<(string name, List<string> kno)>)> getKnowledge(string key, string school, CosmosClient client, string subjectBid, string pId)
+        {
+            try
+            {
+                var response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school, new PartitionKey($"Base"));
+                string subjectId = string.Empty;
+                List<Knowledge> knowledges = new();
+                List<(string name, List<string> kno)> blocks = new();
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    School sc = json.ToObject<School>();
+                    var subjects = sc.period.Where(p => p.id.Equals(pId)).Select(x => x.subjects);
+                    foreach (var sj in subjects)
+                    {
+                        foreach (var s in sj)
+                        {
+                            if (!string.IsNullOrWhiteSpace(s.bindId) && s.bindId.Equals(subjectBid))
+                            {
+                                subjectId = s.id;
+                            }
+                        }
+                    }
+
+                    string code = $"Knowledge-{school}-{subjectId}";
+                    StringBuilder sql = new StringBuilder($"select value(c) from c");
+                    if (!string.IsNullOrWhiteSpace(pId))
+                    {
+                        sql.Append($" where c.periodId = '{pId}'");
+                    }
+
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Knowledge>(queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                    {
+                        knowledges.Add(item);
+                    }
+                }
+                foreach (var know in knowledges)
+                {
+                    foreach (var block in know.blocks)
+                    {
+                        blocks.Add((block.name, block.points));
+                    }
+                }
+                return (key, subjectBid, blocks);
+            }
+            catch (Exception e)
+            {
+                return (null, null, null);
+            }
+        }
+
+    }
+}

+ 457 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerCorrect.cs

@@ -0,0 +1,457 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using System.Text.Json;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.Function;
+
+
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public static class TriggerCorrect
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+           CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis)
+        {
+            if ((tdata.status != null && tdata.status.Value == 404))
+            {
+                await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                ActivityList data = input.ToObject<ActivityList>();
+                //await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                foreach (var record in records)
+                {
+                    try
+                    {
+                        await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                        await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                    }
+                    catch (Exception)
+                    {
+                        continue;
+                    }
+                }
+                return;
+            }
+            var adid = tdata.id;
+            var adcode = "";
+            string blobcntr = null;
+            if (tdata.scope.Equals("school"))
+            {
+                adcode = $"Activity-{tdata.school}";
+                blobcntr = tdata.school;
+            }
+            else
+            {
+                return;
+            }
+            //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}阅卷配置【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在执行", GroupNames.醍摩豆服務運維群組);
+
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+
+            try {
+                Correct correct = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Correct>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+
+                if (correct != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", correct.code, "-", correct.progress);
+                    List<ChangeRecord> correctRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (correct.progress)
+                    {
+                        case "pending":
+                            var messageCorrect = new ServiceBusMessage(new { id = tdata.id, progress = "going", code = tdata.code }.ToJsonString());
+                            messageCorrect.ApplicationProperties.Add("name", "Correct");
+                            if (correctRecords.Count > 0)
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageCorrect, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), correctRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                correctRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(correctRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageCorrect, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new ChangeRecord
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageCorrect.MessageId
+                                };
+                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":
+                            //评测id
+                            string eid = correct.id;
+                            //评测的分区键
+                            string ecode = correct.scode;
+                            ExamInfo info = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(tdata.id, new Azure.Cosmos.PartitionKey($"{ecode}"));
+                            if (correct.subs.IsNotEmpty())
+                            {
+
+                                foreach (var sub in correct.subs)
+                                {
+                                    ///生成阅卷教师的阅卷任务列表
+                                    if (sub.markers.IsNotEmpty())
+                                    {
+                                        foreach (var marker in sub.markers)
+                                        {
+                                            CorrectTask task = new CorrectTask
+                                            {
+                                                ttl = -1,
+                                                pk = "CorrectTask",
+                                                code = "CorrectTask-" + marker.id,
+
+                                                id = Guid.NewGuid().ToString(),
+                                                //评测id 或者阅卷配置id
+                                                cid = correct.id,
+                                                //科目
+                                                subject = sub.id,
+                                                //科目名称
+                                                subjectName = sub.name,
+                                                //评测code
+                                                ecode = correct.scode,
+                                                //阅卷配置code
+                                                scode = correct.code,
+                                                //任务名称
+                                                name = correct.name,
+                                                progress = "going",
+                                                //开始时间
+                                                startTime = correct.startTime,
+                                                //结束时间
+                                                endTime = correct.endTime,
+                                                //批改数量
+                                                count = marker.count,
+                                                //按题阅卷时,题号
+                                                qu = marker.qu,
+                                                //模块数
+                                                model = sub.model,
+                                                type = 1,
+                                                createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                                source = info.source
+                                            };
+                                            await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<CorrectTask>(task, new Azure.Cosmos.PartitionKey(task.code));
+                                        }
+                                    }
+
+
+                                    //生成异常卷处理人员
+                                    if (sub.err.IsNotEmpty())
+                                    {
+
+                                        foreach (var tId in sub.err)
+                                        {
+                                            CorrectTask task = new CorrectTask
+                                            {
+                                                ttl = -1,
+                                                pk = "CorrectTask",
+                                                code = "CorrectTask-" + tId,
+
+                                                id = Guid.NewGuid().ToString(),
+                                                //评测id 或者阅卷配置id
+                                                cid = correct.id,
+                                                //科目
+                                                subject = sub.id,
+                                                //科目名称
+                                                subjectName = sub.name,
+                                                //评测code
+                                                ecode = correct.scode,
+                                                //阅卷配置code
+                                                scode = correct.code,
+                                                progress = "going",
+                                                //任务名称
+                                                name = correct.name,
+                                                //开始时间
+                                                startTime = correct.startTime,
+                                                //结束时间
+                                                endTime = correct.endTime,
+                                                //模块数
+                                                model = sub.model,
+                                                type = 2,
+                                                createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
+                                            };
+                                            await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<CorrectTask>(task, new Azure.Cosmos.PartitionKey(task.code));
+                                        }
+
+
+                                    }
+
+                                    //生成仲裁人员
+
+                                    if (sub.arb.IsNotEmpty())
+                                    {
+
+                                        foreach (var tId in sub.arb)
+                                        {
+                                            CorrectTask task = new CorrectTask
+                                            {
+                                                ttl = -1,
+                                                pk = "CorrectTask",
+                                                code = "CorrectTask-" + tId,
+
+                                                id = Guid.NewGuid().ToString(),
+                                                //评测id 或者阅卷配置id
+                                                cid = correct.id,
+                                                //科目
+                                                subject = sub.id,
+                                                //科目名称
+                                                subjectName = sub.name,
+                                                progress = "going",
+                                                //评测code
+                                                ecode = correct.scode,
+                                                //阅卷配置code
+                                                scode = correct.code,
+                                                //任务名称
+                                                name = correct.name,
+                                                //开始时间
+                                                startTime = correct.startTime,
+                                                //结束时间
+                                                endTime = correct.endTime,
+                                                //模块数
+                                                model = sub.model,
+                                                type = 3,
+                                                createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
+                                            };
+                                            await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<CorrectTask>(task, new Azure.Cosmos.PartitionKey(task.code));
+                                        }
+
+
+                                    }
+                                    //评测科目
+                                    string subjectId = sub.id;
+                                    //生成临时作答数据存放到redis
+                                    //var redisClient = _azureRedis.GetRedisClient(8);
+                                    //ExamInfo info = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(eid, new Azure.Cosmos.PartitionKey(ecode));
+                                    List<ExamClassResult> classResults = new List<ExamClassResult>();
+                                    //获取原题配分
+                                    int paperIndex = 0;
+                                    foreach (ExamSubject subject in info.subjects)
+                                    {
+                                        if (subject.id.Equals(subjectId))
+                                        {
+                                            break;
+                                        }
+                                        else
+                                        {
+                                            paperIndex++;
+                                        }
+                                    }
+                                    List<double> paperPoint = info.papers[paperIndex].point;
+                                    List<List<string>> ans = info.papers[paperIndex].answers;
+                                    if (info.scope.Equals("school"))
+                                    {
+
+                                        await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamClassResult>(
+                                        queryText: $"select value(c) from c where c.examId = '{eid}' and c.subjectId = '{subjectId}'",
+                                        requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{info.school}") }))
+                                        {
+                                            classResults.Add(item);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamClassResult>(
+                                            queryText: $"select value(c) from c where c.examId = '{eid}' and c.subjectId = '{subjectId}'",
+                                            requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{info.creatorId}") }))
+                                        {
+                                            classResults.Add(item);
+                                        }
+                                    }
+                                    List<Task<ItemResponse<SDK.Models.Cosmos.Common.Scoring>>> tasks = new List<Task<ItemResponse<SDK.Models.Cosmos.Common.Scoring>>>();
+                                    //初始化老师阅卷记录
+                                    //List<string> tmds = new List<string>();
+                                    /*                                List<string> marks = new List<string>();
+                                                                    for (int i = 0; i < correct.num; i++)
+                                                                    {
+                                                                        marks.Add("");
+                                                                    }*/
+                                    foreach (ExamClassResult examClass in classResults)
+                                    {
+                                        foreach (string stuId in examClass.studentIds)
+                                        {
+                                            int index = examClass.studentIds.IndexOf(stuId);
+                                            if (index > -1)
+                                            {
+                                                List<double> scc = examClass.studentScores[index];
+                                                List<Item> items = new List<Item>();
+                                                List<Qs> qss = new List<Qs>();
+                                                int itemIndex = 0;
+                                                foreach (double psc in scc)
+                                                {
+                                                    //初始化异常卷信息,初始化仲裁卷信息
+                                                    Qs qs = new Qs();
+                                                    List<Info> infos = new List<Info>();
+                                                    Item item = new Item
+                                                    {
+                                                        ssc = paperPoint[itemIndex],
+                                                        scores = infos
+                                                    };
+                                                    itemIndex++;
+                                                    items.Add(item);
+                                                    qss.Add(qs);
+                                                }
+                                                //处理学生未作答 生成阅卷数据时 客观题分数为-1的情况
+                                                List<double> scores = new List<double>();
+                                                int n = 0;
+                                                foreach (List<string> answer in ans)
+                                                {
+                                                    var scs = examClass.studentScores[index][n];
+
+                                                    if (answer.Count > 0)
+                                                    {
+                                                        if (scs == -1)
+                                                        {
+                                                            scores.Add(0);
+                                                        }
+                                                        else
+                                                        {
+                                                            scores.Add(scs);
+                                                        }
+                                                    }
+                                                    else
+                                                    {
+                                                        scores.Add(scs);
+                                                    }
+                                                    n++;
+                                                }
+                                                SDK.Models.Cosmos.Common.Scoring sc = new SDK.Models.Cosmos.Common.Scoring
+                                                {
+                                                    id = Guid.NewGuid().ToString(),
+                                                    code = "Scoring-" + info.school,
+                                                    blob = examClass.studentAnswers[index].Count > 0 ? examClass.studentAnswers[index][0] : "",
+                                                    stuId = stuId,
+                                                    examId = eid,
+                                                    subjectId = subjectId,
+                                                    scores = scores,
+                                                    count = correct.num,
+                                                    //marks = marks,
+                                                    items = items,
+                                                    qs = qss,
+                                                    //endTime = correct.endTime,
+                                                    model = sub.model
+
+                                                };
+                                                tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<SDK.Models.Cosmos.Common.Scoring>(sc, new Azure.Cosmos.PartitionKey(sc.code)));
+                                            }
+                                            //tasks.Add(redisClient.HashSetAsync($"Exam:Scoring:{eid}-{subjectId}", stuId, new { tmdId = tmds, ans = examClass.studentAnswers[index].Count > 0 ? examClass.studentAnswers[index][0] : "", score = examClass.studentScores[index] }.ToJsonString()));
+                                        }
+
+                                    }
+                                    await Task.WhenAll(tasks);
+                                }
+                            }
+                            var messageCorrectEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                            messageCorrectEnd.ApplicationProperties.Add("name", "Correct");
+                            if (correctRecords.Count > 0)
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageCorrectEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), correctRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                correctRecords[0].sequenceNumber = end;
+                                await table.SaveOrUpdate<ChangeRecord>(correctRecords[0]);
+                            }
+                            else
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageCorrectEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                ChangeRecord changeRecord = new ChangeRecord
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = end,
+                                    msgId = messageCorrectEnd.MessageId
+                                };
+                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "finish":
+                            foreach (var sub in correct.subs)
+                            {
+                                List<string> ids = new List<string>();
+                                ///阅卷教师的阅卷任务列表
+                                if (sub.markers.IsNotEmpty())
+                                {
+                                    foreach (var marker in sub.markers)
+                                    {
+                                        ids.Add(marker.id);
+                                        //await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<CorrectTask>(task, new Azure.Cosmos.PartitionKey(task.code),new ItemRequestOptions().PostTriggers);
+                                    }
+                                }
+                                //异常卷处理人员
+                                if (sub.err.IsNotEmpty())
+                                {
+
+                                    foreach (var tId in sub.err)
+                                    {
+                                        if (!ids.Contains(tId))
+                                        {
+                                            ids.Add(tId);
+                                        }
+                                    }
+                                }
+
+                                //仲裁人员
+
+                                if (sub.arb.IsNotEmpty())
+                                {
+                                    foreach (var tId in sub.arb)
+                                    {
+                                        if (!ids.Contains(tId))
+                                        {
+                                            ids.Add(tId);
+                                        }
+                                    }
+                                }
+                                List<CorrectTask> corrects = new List<CorrectTask>();
+                                foreach (string id in ids)
+                                {
+                                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<CorrectTask>(
+                                          queryText: $"select value(c) from c where c.cid = '{correct.id}'",
+                                          requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"CorrectTask-{id}") }))
+                                    {
+                                        corrects.Add(item);
+                                    }
+                                }
+                                List<Task<ItemResponse<CorrectTask>>> tasks = new List<Task<ItemResponse<CorrectTask>>>();
+                                foreach (CorrectTask task in corrects)
+                                {
+                                    task.progress = "finish";
+                                    tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(task, task.id, new Azure.Cosmos.PartitionKey(task.code)));
+                                }
+                                await Task.WhenAll(tasks);
+                            }
+                            break;
+                    }
+                }
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-阅卷异常{e.Message}\n{e.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+            }
+            
+        }
+    }
+}

File diff suppressed because it is too large
+ 2225 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerExam.cs


+ 176 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerExamImport.cs

@@ -0,0 +1,176 @@
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using Azure.Cosmos;
+using System.Net.Http;
+using TEAMModelOS.SDK.Models.Cosmos.School;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public class TriggerExamImport
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+            CosmosClient client, JsonElement input, TriggerData data, IHttpClientFactory _httpClient, IConfiguration _configuration,AzureRedisFactory _azureRedis)
+        { 
+            ExamImport examImport= input.ToObject<ExamImport>();
+            if (examImport != null) {
+                HashSet<string> ids = new HashSet<string>();
+                foreach (var x in examImport.subjects)
+                {
+                    foreach (var y in x.students)
+                    {
+                        string id = $"{examImport.year}-{examImport.semesterId}-{y.id}";
+                        ids.Add(id);
+                    }
+                }
+                HashSet<OverallEducation> overallEducations = new HashSet<OverallEducation>();
+                string sql = $"select value c from  c where c.id in ({string.Join(",", ids.Select(z => $"'{z}'"))}) and c.periodId='{examImport.periodId}' ";
+                var result = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetList<OverallEducation>(sql, $"OverallEducation-{examImport.school}");
+                IEnumerable<string> notInDbIds = null;
+                if (result.list.IsNotEmpty())
+                {
+                    notInDbIds= ids.Except(result.list.Select(x => x.id));
+                    overallEducations = new HashSet<OverallEducation>(result.list);
+                }
+                else {
+                    notInDbIds = ids;
+                }
+
+                HashSet<OverallEducation> overallEducationChanged = new HashSet<OverallEducation>();
+                foreach (var x in examImport.subjects)
+                {
+                    foreach (var y in x.students)
+                    {
+                        string id = $"{examImport.year}-{examImport.semesterId}-{y.id}";
+                        var overallEducation =  overallEducations.Where(z => z.id.Equals(id)).FirstOrDefault();
+                        if (overallEducation != null)
+                        {
+                            EducationScore exam = null;
+                            if (x.id.Equals("subject_music") || x.id.Equals("subject_painting"))
+                            {
+                                exam = overallEducation.art.Find(f => f.examId.Equals(examImport.id));
+                            }
+                            else {
+                                 exam = overallEducation.intelligence.Find(f => f.examId.Equals(examImport.id));
+                            }
+                            if (exam != null)
+                            {
+                                var item = exam.itemScore.Find(f => f.id.Equals(x.id));
+                                if (item != null)
+                                {
+                                    item.name= x.name;
+                                    if (item.score != y.score) {
+                                        overallEducationChanged.Add(overallEducation);
+                                        item.score = y.score;
+                                    }
+                                    if (item.totalScore != x.items.Sum(b => b.score)) {
+                                        overallEducationChanged.Add(overallEducation);
+                                        item.totalScore = x.items.Sum(b => b.score);
+                                    }
+                                    item.type = x.name;
+                                }
+                                else {
+                                    overallEducationChanged.Add(overallEducation);
+                                    exam.itemScore.Add(new ItemScore { name = x.name, score = y.score, totalScore = x.items.Sum(b => b.score), type = x.name, id = x.id , time=examImport.time});
+                                }
+                                if (exam.sumScore != exam.itemScore.Sum(x => x.score)) {
+                                    overallEducationChanged.Add(overallEducation);
+                                    //exam.sumScore = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score);
+                                    exam.sumScore = exam.itemScore.Sum(x => x.score);
+                                }
+                                if (exam.totalScore != exam.itemScore.Sum(x => x.totalScore))
+                                {
+                                    overallEducationChanged.Add(overallEducation);
+                                    exam.totalScore = exam.itemScore.Sum(x => x.totalScore);
+                                    // exam.totalScore = examImport.subjects.SelectMany(t => t.items).Sum(n => n.score);
+                                }
+                                if (exam.rate != examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score) * 1.0 / examImport.subjects.SelectMany(t => t.items).Sum(n => n.score))
+                                {
+                                    overallEducationChanged.Add(overallEducation);
+                                    exam.rate = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score) * 1.0 / examImport.subjects.SelectMany(t => t.items).Sum(n => n.score);
+                                }
+                                exam.examType = examImport.type;
+                                exam.examDate = examImport.time;
+                                exam.examName=examImport.name;
+                            }
+                            else {
+
+                                exam = new EducationScore
+                                {
+                                    examName = examImport.name,
+                                    examId = examImport.id,
+                                    examDate = examImport.time,
+                                    examType = examImport.type,
+                                    //totalScore = examImport.subjects.SelectMany(t => t.items).Sum(n => n.score),
+                                    //sumScore = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score),
+                                    rate = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score) * 1.0 / examImport.subjects.SelectMany(t => t.items).Sum(n => n.score),
+                                    itemScore = new List<ItemScore> { new ItemScore { name = x.name, score = y.score, totalScore = x.items.Sum(b => b.score), type = x.name, time = examImport.time, id = x.id } }
+                                };
+                                exam.totalScore = exam.itemScore.Sum(x => x.totalScore);
+                                exam.sumScore = exam.itemScore.Sum(x => x.score);
+                                if (x.id.Equals("subject_music") || x.id.Equals("subject_painting"))
+                                {
+                                    overallEducation.art.Add(exam);
+                                }
+                                else
+                                {
+                                    overallEducation.intelligence.Add(exam);
+                                }
+                                
+                                overallEducationChanged.Add(overallEducation);
+                            }
+                        }
+                        else {
+
+                            EducationScore exam = new EducationScore
+                            {
+                                examName = examImport.name,
+                                examId = examImport.id,
+                                examDate = examImport.time,
+                                examType = examImport.type,
+                                //totalScore = examImport.subjects.SelectMany(t => t.items).Sum(n => n.score),
+                                //sumScore = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score),
+                                rate = examImport.subjects.SelectMany(z => z.students).Where(b => b.id.Equals(y.id)).Sum(v => v.score) * 1.0 / examImport.subjects.SelectMany(t => t.items).Sum(n => n.score),
+                                itemScore = new List<ItemScore> { new ItemScore { id = x.id, time = examImport.time, name = x.name, score = y.score, totalScore = x.items.Sum(b => b.score), type = x.name } }
+                            };
+                            exam.totalScore = exam.itemScore.Sum(x => x.totalScore);
+                            exam.sumScore = exam.itemScore.Sum(x => x.score);
+                            overallEducation = new OverallEducation {
+                                id = id,
+                                code = $"OverallEducation-{examImport.school}",
+                                pk = "OverallEducation",
+                                periodId = examImport.periodId,
+                                year = examImport.year,
+                                semesterId = examImport.semesterId,
+                                schoolCode = examImport.school,
+                                studentId = y.id,
+                                name = y.name,
+                                classId = y.classId,
+                                stuYear = y.stuYear,
+                                intelligence = x.id.Equals("subject_music") || x.id.Equals("subject_painting") ? new List<EducationScore> { }:new List<EducationScore> { exam },
+                                art= x.id.Equals("subject_music") || x.id.Equals("subject_painting") ? new List<EducationScore> { exam } : new List<EducationScore> {  },
+                            };
+                            overallEducationChanged.Add(overallEducation);
+                            overallEducations.Add(overallEducation);
+                        }
+                    }
+                }
+                foreach (var item in overallEducationChanged) { 
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,Constant.Student).UpsertItemAsync(item,new PartitionKey(item.code));
+                    string key = $"OverallEducation:{item.schoolCode}:{item.periodId}:{item.year}:{item.semesterId}:{item.classId}";
+                    await _azureRedis.GetRedisClient(8).HashSetAsync(key, item.studentId, item.ToJsonString());
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 * 24, 0, 0));
+                }
+            }
+        }
+    }
+}

+ 224 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerExamLite.cs

@@ -0,0 +1,224 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Azure.Documents;
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Service;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public static class TriggerExamLite
+    {
+        public static async Task Trigger (CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+                    CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404))
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                   // await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                           
+                        }
+                        catch (Exception)
+                        {
+                            continue;
+                        }
+                    }
+                    return;
+                }
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+               // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修评测活动【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在操作", GroupNames.醍摩豆服務運維群組);
+                ExamLite lite = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamLite>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                if (lite != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", lite.code, "-", lite.progress);
+                    List<ChangeRecord> changeRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (lite.progress)
+                    {
+                        case "pending":
+                            var messageWork = new ServiceBusMessage(new { id = tdata.id, progress = "going", code = tdata.code }.ToJsonString());
+                            messageWork.ApplicationProperties.Add("name", "ExamLite");
+                            if (changeRecords.Count > 0)
+                            {
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                changeRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageWork.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":
+                            List<(string pId, List<string> gid)> ps = new();
+                            if (lite.groupLists.Count > 0)
+                            {
+                                var group = lite.groupLists;
+                                foreach (var gp in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in gp)
+                                    {
+                                        ps.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            (List<RMember> tchList, List<RGroupList> classInfos) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, lite.tchLists, lite.school, ps);
+                            //List<StuActivity> tchActivities = new();
+                            (string standard, List<string> tmdids, string school, List<string> update, int statistics)  list =    (null,null, null, new List<string> { StatisticsService.TeacherExamLite }, 0)  ;
+                            if (tchList.IsNotEmpty())
+                            {
+                                list.tmdids = tchList.Select(x => x.id).ToList();
+                                School school = null;
+                                if (!string.IsNullOrEmpty(lite.school))
+                                {
+                                    school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(lite.school, new Azure.Cosmos.PartitionKey("Base"));
+                                    list.school = school.id;
+                                    list.standard = school.standard;
+                                }
+                                //tchList.ForEach(x =>
+                                //{
+                                //    tchActivities.Add(new StuActivity
+                                //    {
+                                //        pk = "Activity",
+                                //        id = lite.id,
+                                //        code = $"Activity-{x.id}",
+                                //        type = "ExamLite",
+                                //        name = lite.name,
+                                //        startTime = lite.startTime,
+                                //        endTime = lite.endTime,
+                                //        scode = lite.code,
+                                //        scope = lite.scope,
+                                //        school = lite.school,
+                                //        creatorId = lite.creatorId,
+                                //        subjects = new List<string> { "" },
+                                //        blob = null,
+                                //        owner = lite.owner,
+                                //        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                //        taskStatus = -1,
+                                //        classIds = lite.tchLists
+                                //    });
+                                //});
+                              
+                            }
+                            //await IESActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
+                            await StatisticsService.SendServiceBus(list, _configuration, _serviceBus,   client);
+                            var messageWorkEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                            messageWorkEnd.ApplicationProperties.Add("name", "ExamLite");
+                            if (changeRecords.Count > 0)
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                changeRecords[0].sequenceNumber = end;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = end,
+                                    msgId = messageWorkEnd.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "finish":
+
+                            List<(string pId, List<string> gid)> gls = new List<(string pId, List<string> gid)>();
+                            if (lite.groupLists.Count > 0)
+                            {
+                                var group = lite.groupLists;
+                                foreach (var gp in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in gp)
+                                    {
+                                        gls.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            if (lite.staffIds.Count == 0)
+                            {
+                            // 处理试卷练习活动结束统计账户信息
+                                List<FMember> idList = await GroupListService.GetFinishMemberInfo(_coreAPIHttpService, client, _dingDing, lite.school, lite.classes, lite.stuLists, lite.tchLists, gls);
+                                lite.staffIds = idList;
+
+                                await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<ExamLite>(lite, lite.id, new Azure.Cosmos.PartitionKey(lite.code));
+                            }
+                                
+
+                            break;
+                    }
+
+
+                }
+
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修评测异常{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+            }
+
+        }
+    }
+}

+ 412 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerHomework.cs

@@ -0,0 +1,412 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Azure.Documents;
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Service;
+using TEAMModelOS.SDK.Models.Service.BI;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public static class TriggerHomework
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+                    CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404))
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                    //await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+
+                        }
+                        catch (Exception)
+                        {
+                            continue;
+                        }
+                    }
+
+                    await BIStats.SetTypeAddStats(client, _dingDing, tdata.school, "Homework", -1, careDate: tdata.startTime);//BI统计增/减量
+                    return;
+                }
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+                //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}作业活动【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在操作", GroupNames.醍摩豆服務運維群組);
+                Homework work = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Homework>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                if (work != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", work.code, "-", work.progress);
+                    List<ChangeRecord> changeRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (work.progress)
+                    {
+                        case "pending":
+                            var messageWork = new ServiceBusMessage(new { tdata.id, progress = "going", tdata.code }.ToJsonString());
+                            messageWork.ApplicationProperties.Add("name", "Homework");
+                            if (changeRecords.Count > 0)
+                            {
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                changeRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageWork.MessageId
+                                };
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+
+                            break;
+                        case "going":
+                            try {
+                                await Activity(_coreAPIHttpService, _serviceBus, _dingDing, client, _configuration, work);
+                                var messageWorkEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                                messageWorkEnd.ApplicationProperties.Add("name", "Homework");
+                                if (changeRecords.Count > 0)
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    try
+                                    {
+                                        await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                    }
+                                    catch (Exception)
+                                    {
+                                    }
+                                    changeRecords[0].sequenceNumber = end;
+                                    await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                                }
+                                else
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    ChangeRecord changeRecord = new()
+                                    {
+                                        RowKey = tdata.id,
+                                        PartitionKey = PartitionKey,
+                                        sequenceNumber = end,
+                                        msgId = messageWorkEnd.MessageId
+                                    };
+                                    await table.Save<ChangeRecord>(changeRecord);
+                                }
+                                if (work!=null && work.scope.Equals("private"))
+                                {
+                                    await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client="web", count=1, id=work.id, key="homework-going", name=work.name, scope="teacher", target=work.creatorId });
+                                }
+                            } catch (Exception e) {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修作业活动going{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                            }
+                            finally {
+                                string pkey = string.Format("{0}{1}{2}", work.code, "-", "pending");
+                                await table.DeleteSingle<ChangeRecord>(pkey, tdata.id);
+                            }
+                            
+                            
+                            //if (bustasks.IsNotEmpty())
+                            //{
+                            //    await Task.WhenAll(bustasks);
+                            //}
+                            break;
+                        case "finish":
+                            try {
+                                await Activity(_coreAPIHttpService, _serviceBus, _dingDing, client, _configuration, work);
+                                List<(string pId, List<string> gid)> gls = new List<(string pId, List<string> gid)>();
+                                if (work.groupLists.Count > 0)
+                                {
+                                    var group = work.groupLists;
+                                    foreach (var gp in group)
+                                    {
+                                        foreach (KeyValuePair<string, List<string>> pp in gp)
+                                        {
+                                            gls.Add((pp.Key, pp.Value));
+                                        }
+                                    }
+                                }
+                                //处理家庭作业活动结束统计账户信息
+                                if (work.staffIds.Count == 0)
+                                {
+                                    List<FMember> idsList = await GroupListService.GetFinishMemberInfo(_coreAPIHttpService, client, _dingDing, work.school, work.classes, work.stuLists, work.tchLists, gls);
+                                    work.staffIds = idsList;
+
+                                    await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<Homework>(work, work.id, new Azure.Cosmos.PartitionKey(work.code));
+                                }
+                            } catch (Exception e) {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修作业活动finish{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
+
+                            } finally {
+                                string pk = string.Format("{0}{1}{2}", work.code, "-", "going");
+                                await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            }
+                            
+                                                
+                            break;
+                    }
+
+                }
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修作业活动异常{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+            }
+        }
+
+        private static async Task Activity(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, DingDing _dingDing, CosmosClient client, IConfiguration _configuration, Homework work)
+        {
+            List<(string pId, List<string> gid)> ps = new List<(string pId, List<string> gid)>();
+            if (work.groupLists.Count > 0)
+            {
+                var group = work.groupLists;
+                foreach (var gp in group)
+                {
+                    foreach (KeyValuePair<string, List<string>> pp in gp)
+                    {
+                        ps.Add((pp.Key, pp.Value));
+                    }
+                }
+            }
+            List<string> classes = ExamService.getClasses(work.classes, work.stuLists);
+            (List<RMember> tmdids, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, work.school, ps);
+            var addStudentsCls = tmdids.FindAll(x => x.type == 2);
+            var addTmdidsCls = tmdids.FindAll(x => x.type == 1);
+            //List<StuActivity> stuActivities = new();
+            //List<StuActivity> tmdActivities = new();
+            //List<StuActivity> tchActivities = new();
+            //List<StuActivity> tac = new();
+            List<string> tIds = addStudentsCls.Select(x => x.id).ToList();
+            /*
+                         await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<StuActivity>(queryText: $"select value(c) from c where c.id ='{work.id}' and c.type = 'Homework'"))
+            {
+                tac.Add(item);
+
+            }
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<StuActivity>(queryText: $"select value(c) from c where c.id ='{work.id}' and c.type = 'Homework'"))
+            {
+                tac.Add(item);
+
+            }
+            List<string> sub = new();
+            if (work.tchLists.Count == 0)
+            {
+                if (work.targets.Count > 0)
+                {
+                    foreach (var course in work.targets)
+                    {
+                        var info = course.ToObject<List<string>>();
+                        if (info.Count > 1)
+                        {
+                            sub.Add(info[0]);
+                        }
+                    }
+                }
+            }
+
+            if (addTmdidsCls.IsNotEmpty())
+            {
+                addTmdidsCls.ForEach(x =>
+                {
+                    HashSet<string> classIds = new HashSet<string>();
+                    classLists.ForEach(z =>
+                    {
+                        z.members.ForEach(y =>
+                        {
+                            if (y.id.Equals(x.id) && y.type == 1)
+                            {
+                                classIds.Add(z.id);
+                            }
+                        });
+                    });
+                    int sta = -1;
+                    if (tac.Count > 0)
+                    {
+                        StuActivity activity = tac.Where(t => t.code.Equals($"Activity-{x.id}")).FirstOrDefault();
+                        if (activity != null)
+                        {
+                            sta = activity.taskStatus;
+                        }
+                    }
+                    tmdActivities.Add(new StuActivity
+                    {
+                        pk = "Activity",
+                        id = work.id,
+                        code = $"Activity-{x.id}",
+                        type = "Homework",
+                        name = work.name,
+                        startTime = work.startTime,
+                        endTime = work.endTime,
+                        scode = work.code,
+                        scope = work.scope,
+                        school = work.school,
+                        creatorId = work.creatorId,
+                        subjects = sub,
+                        blob = work.blob,
+                        owner = work.owner,
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                        taskStatus = sta,
+                        mustSubmit = work.mustSubmit,
+                        classIds = classIds.ToList()
+                    });
+                });
+            }
+            if (addStudentsCls.IsNotEmpty())
+            {
+                addStudentsCls.ForEach(x =>
+                {
+                    HashSet<string> classIds = new HashSet<string>();
+                    classLists.ForEach(z =>
+                    {
+                        z.members.ForEach(y =>
+                        {
+                            if (y.id.Equals(x.id) && y.type == 2)
+                            {
+                                classIds.Add(z.id);
+                            }
+                        });
+                    });
+                    int sta = -1;
+                    if (tac.Count > 0)
+                    {
+                        StuActivity activity = tac.Where(t => t.code.Equals($"Activity-{x.code.Replace("Base-", "")}-{x.id}")).FirstOrDefault();
+                        if (activity != null)
+                        {
+                            sta = activity.taskStatus;
+                        }
+                    }
+
+                    stuActivities.Add(new StuActivity
+                    {
+                        pk = "Activity",
+                        id = work.id,
+                        code = $"Activity-{x.code.Replace("Base-", "")}-{x.id}",
+                        type = "Homework",
+                        name = work.name,
+                        startTime = work.startTime,
+                        endTime = work.endTime,
+                        scode = work.code,
+                        scope = work.scope,
+                        school = work.school,
+                        creatorId = work.creatorId,
+                        subjects = sub,
+                        blob = work.blob,
+                        owner = work.owner,
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                        taskStatus = sta,
+                        classIds = classIds.ToList(),
+                        mustSubmit = work.mustSubmit
+                    });
+                });
+            }
+             */
+            (List<RMember> tchList, List<RGroupList> classInfos) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, work.tchLists, work.school, ps);
+            (string standard, List<string> tmdids, string school, List<string> update, int statistics) list = (null, null, null, new List<string> { StatisticsService.OfflineRecord }, 0);
+            if (tchList.IsNotEmpty())
+            {
+                list.tmdids = tchList.Select(x => x.id).ToList();
+                School school = null;
+                if (!string.IsNullOrEmpty(work.school))
+                {
+                    school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(work.school, new Azure.Cosmos.PartitionKey("Base"));
+                    list.school = school.id;
+                    list.standard = school.standard;
+                }
+                /*
+                 tchList.ForEach(x =>
+                {
+                    HashSet<string> classIds = new();
+                    classInfos.ForEach(z =>
+                    {
+                        z.members.ForEach(y =>
+                        {
+                            if (y.id.Equals(x.id) && y.type == 1)
+                            {
+                                classIds.Add(z.id);
+                            }
+                        });
+                    });
+                    int sta = -1;
+                    if (tac.Count > 0)
+                    {
+                        StuActivity activity = tac.Where(t => t.code.Equals($"Activity-{x.id}")).FirstOrDefault();
+                        if (activity != null)
+                        {
+                            sta = activity.taskStatus;
+                        }
+                    }
+                    tchActivities.Add(new StuActivity
+                    {
+                        pk = "Activity",
+                        id = work.id,
+                        code = $"Activity-{x.id}",
+                        type = "Homework",
+                        name = work.name,
+                        startTime = work.startTime,
+                        endTime = work.endTime,
+                        scode = work.code,
+                        scope = work.scope,
+                        school = work.school,
+                        creatorId = work.creatorId,
+                        subjects = new List<string> { "" },
+                        blob = work.blob,
+                        owner = work.owner,
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                        taskStatus = sta,
+                        classIds = classIds.ToList()
+                    });
+
+                });
+                 */
+            }
+            //await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+            await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
+
+        }
+
+    }
+}
+

+ 240 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerQuotaImport.cs

@@ -0,0 +1,240 @@
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using Azure.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.School;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Extension;
+using HTEXLib.COMM.Helpers;
+using OpenXmlPowerTools;
+using Microsoft.OData.Edm;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public class TriggerQuotaImport
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+           CosmosClient client, JsonElement input, TriggerData data, IHttpClientFactory _httpClient, IConfiguration _configuration,AzureRedisFactory _azureRedis)
+        {
+            QuotaImport quotaImport = input.ToObject<QuotaImport>();
+            if (quotaImport != null)
+            {
+                HashSet<string> ids = new HashSet<string>();
+                foreach (var x in quotaImport.students)
+                {
+                    string id = $"{quotaImport.year}-{quotaImport.semesterId}-{x.id}";
+                    ids.Add(id);
+                }
+                HashSet<OverallEducation> overallEducations = new HashSet<OverallEducation>();
+                string sql = $"select value c from  c where c.id in ({string.Join(",", ids.Select(z => $"'{z}'"))}) and c.periodId='{quotaImport.periodId}' ";
+                var result = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetList<OverallEducation>(sql, $"OverallEducation-{quotaImport.school}");
+                IEnumerable<string> notInDbIds = null;
+                if (result.list.IsNotEmpty())
+                {
+                    notInDbIds = ids.Except(result.list.Select(x => x.id));
+                    overallEducations = new HashSet<OverallEducation>(result.list);
+                }
+                else
+                {
+                    notInDbIds = ids;
+                }
+                HashSet<OverallEducation> overallEducationChanged = new HashSet<OverallEducation>();
+                foreach (var y in quotaImport.students)
+                {
+                    string id = $"{quotaImport.year}-{quotaImport.semesterId}-{y.id}";
+                    var overallEducation = overallEducations.Where(z => z.id.Equals(id)).FirstOrDefault();
+                    if (overallEducation != null)
+                    {
+                        EducationScore quota = null;
+                        if (quotaImport.dimension.Equals("virtue"))
+                        {
+                            quota = overallEducation.virtue.Find(f => f.examId.Equals($"{overallEducation.stuYear}-{overallEducation.semesterId}"));
+                        }
+                        else if (quotaImport.dimension.Equals("labour")) {
+                            quota = overallEducation.labour.Find(f => f.examId.Equals($"{overallEducation.stuYear}-{overallEducation.semesterId}"));
+                        }
+                        else if (quotaImport.dimension.Equals("sports"))
+                        {
+                            quota = overallEducation.sports.Find(f => f.examId.Equals($"{overallEducation.stuYear}-{overallEducation.semesterId}"));
+                        }
+                        if (quota != null)
+                        {
+                            //体育成绩直接覆盖
+                            if (quotaImport.dimension.Equals("sports"))
+                            {
+                                List<ItemScore> itemScores = y.items.Select(z => new ItemScore
+                                {
+                                    totalScore = !quotaImport.dimension.Equals("sports") ? 0 : 100,
+                                    name = z.code,
+                                    score = z.value,
+                                    type = z.code,
+                                    id = !quotaImport.dimension.Equals("sports") ? $"{y.date}-{z.code}" : z.code,
+                                    time = y.time
+                                }).ToList();
+                                quota.itemScore = itemScores;
+                                overallEducationChanged.Add(overallEducation);
+                            }
+                            else {
+                                y.items.ForEach(z => {
+                                    var item = quota.itemScore.Find(f => f.id.Equals(!quotaImport.dimension.Equals("sports") ? $"{y.date}-{z.code}" : z.code));
+                                    if (item != null)
+                                    {
+                                        item.name = z.code;
+                                        if (item.score != z.value)
+                                        {
+                                            item.score = z.value;
+
+
+                                            overallEducationChanged.Add(overallEducation);
+                                        }
+                                        item.totalScore = !quotaImport.dimension.Equals("sports") ? 0 : 100;
+                                        item.type = z.code;
+                                        item.time = y.time;
+                                        item.id = !quotaImport.dimension.Equals("sports") ? $"{y.date}-{z.code}" : z.code;
+                                    }
+                                    else
+                                    {
+                                        overallEducationChanged.Add(overallEducation);
+                                        quota.itemScore.Add(new ItemScore
+                                        {
+                                            totalScore = !quotaImport.dimension.Equals("sports") ? 0 : 100,
+                                            name = z.code,
+                                            score = z.value,
+                                            type = z.code,
+                                            id = id = !quotaImport.dimension.Equals("sports") ? $"{y.date}-{z.code}" : z.code,
+                                            time = y.time
+                                        });
+                                    }
+
+                                });
+                            }
+
+                            if (quota.sumScore != quota.itemScore.Sum(z => z.score)) {
+                                overallEducationChanged.Add(overallEducation);
+                                quota.sumScore = quota.itemScore.Sum(z => z.score);
+                            }
+                            if (quota.totalScore != quota.itemScore.Sum(z => z.totalScore))
+                            {
+                                overallEducationChanged.Add(overallEducation);
+                                quota.totalScore = quota.itemScore.Sum(z => z.totalScore);
+                            }
+                            quota.rate = quotaImport.dimension.Equals("sports") && quota.itemScore.Sum(z => z.totalScore) > 0 ? quota.itemScore.Sum(z => z.score) * 1.0 / quota.itemScore.Sum(z => z.totalScore) : 0;
+                            quota.excellenceRate = quotaImport.dimension.Equals("sports") && quota.itemScore.Sum(z => z.totalScore) > 0 ? quota.itemScore.Where(z => z.score >= 90).Sum(z => z.score) * 1.0 / quota.itemScore.Sum(z => z.totalScore) : 0;
+                            quota.passRate = quotaImport.dimension.Equals("sports") && quota.itemScore.Sum(z => z.totalScore) > 0 ? quota.itemScore.Where(z => z.score >= 60).Sum(z => z.score) * 1.0 / quota.itemScore.Sum(z => z.totalScore) : 0;
+                            quota.examType = quotaImport.type;
+                            quota.examDate = quotaImport.time;
+                            quota.examName = $"{overallEducation.stuYear}-{overallEducation.semesterId}";
+                        }
+                        else
+                        {
+                            List<ItemScore> itemScores = y.items.Select(z =>new ItemScore {
+                                totalScore = !quotaImport.dimension.Equals("sports")  ? 0 : 100,
+                                name = z.code,
+                                score = z.value,
+                                type = z.code,
+                                id = !quotaImport.dimension.Equals("sports")  ? $"{y.date}-{z.code}" : z.code,
+                                time = y.time
+                            }).ToList();
+                            EducationScore educationScore = new EducationScore
+                            {
+                                examName = $"{overallEducation.stuYear}-{overallEducation.semesterId}",
+                                examId = $"{overallEducation.stuYear}-{overallEducation.semesterId}",
+                                examDate = quotaImport.time,
+                                examType = quotaImport.type,
+                                sumScore = itemScores.Sum(z => z.score),
+                                itemScore = itemScores,
+                                totalScore = itemScores.Sum(z => z.totalScore),
+                                rate = quotaImport.dimension.Equals("sports") && itemScores.Sum(z => z.totalScore) > 0 ? itemScores.Sum(z => z.score) * 1.0 / itemScores.Sum(z => z.totalScore) : 0,
+                                excellenceRate = quotaImport.dimension.Equals("sports") && itemScores.Sum(z => z.totalScore) > 0 ? itemScores.Where(z => z.score >= 90).Sum(z => z.score) * 1.0 / itemScores.Sum(z => z.totalScore) : 0,
+                                passRate = quotaImport.dimension.Equals("sports") && itemScores.Sum(z => z.totalScore) > 0 ? itemScores.Where(z => z.score >= 60).Sum(z => z.score) * 1.0 / itemScores.Sum(z => z.totalScore) : 0,
+                            };
+                            if (quotaImport.dimension.Equals("virtue"))
+                            {
+                                overallEducation.virtue.Add(educationScore);
+                            }
+                            else if (quotaImport.dimension.Equals("labour"))
+                            {
+                                overallEducation.labour.Add(educationScore);
+                            }
+                            else if (quotaImport.dimension.Equals("sports"))
+                            {
+                                overallEducation.sports.Add(educationScore);
+                            }
+                            overallEducationChanged.Add(overallEducation);
+                        }
+                    }
+                    else
+                    {
+                        
+                        List<ItemScore> itemScores = y.items.Select(z => new ItemScore {
+                            totalScore = !quotaImport.dimension.Equals("sports")  ? 0 : 100,
+                            name = z.code,
+                            score = z.value,
+                            type = z.code,
+                            id = !quotaImport.dimension.Equals("sports")  ? $"{y.date}-{z.code}" : z.code,
+                            time = y.time
+                        }
+                        ).ToList();
+                        var educationScores = new List<EducationScore>
+                        {
+                            new EducationScore
+                            {
+                                examName=$"{y.stuYear}-{quotaImport.semesterId}",
+                                examId =$"{y.stuYear}-{quotaImport.semesterId}",
+                                examDate=quotaImport.time,
+                                examType=quotaImport.type,
+                                sumScore = itemScores.Sum(z => z.score),
+                                itemScore = itemScores,
+                                totalScore = itemScores.Sum(z => z.totalScore),
+                                rate= quotaImport.dimension.Equals("sports") && itemScores.Sum(z => z.totalScore)>0 ? itemScores.Sum(z => z.score) *1.0 / itemScores.Sum(z => z.totalScore):0,
+                                excellenceRate=quotaImport.dimension.Equals("sports")&& itemScores.Sum(z => z.totalScore)>0 ? itemScores.Where(z => z.score >= 90).Sum(z => z.score) *1.0 / itemScores.Sum(z => z.totalScore):0,
+                                passRate= quotaImport.dimension.Equals("sports") && itemScores.Sum(z => z.totalScore)>0 ? itemScores.Where(z => z.score >= 60).Sum(z => z.score) *1.0 / itemScores.Sum(z => z.totalScore):0,
+                            }
+                        };
+                        overallEducation = new OverallEducation
+                        {
+                            id = id,
+                            code = $"OverallEducation-{quotaImport.school}",
+                            pk = "OverallEducation",
+                            periodId = quotaImport.periodId,
+                            year = quotaImport.year,
+                            semesterId = quotaImport.semesterId,
+                            schoolCode = quotaImport.school,
+                            studentId = y.id,
+                            name = y.name,
+                            classId = y.classId,
+                            stuYear = y.stuYear
+                        };
+                        if (quotaImport.dimension.Equals("virtue"))
+                        {
+                            overallEducation.virtue= educationScores;
+                        }
+                        else if (quotaImport.dimension.Equals("labour"))
+                        {
+                            overallEducation.labour= educationScores;
+                        }
+                        else if (quotaImport.dimension.Equals("sports"))
+                        {
+                            overallEducation.sports= educationScores;
+                        }
+                        overallEducationChanged.Add(overallEducation);
+                        overallEducations.Add(overallEducation);
+                    }
+                }
+                foreach (var item in overallEducationChanged) { 
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,Constant.Student).UpsertItemAsync(item,new PartitionKey (item.code));
+                    string key = $"OverallEducation:{item.schoolCode}:{item.periodId}:{item.year}:{item.semesterId}:{item.classId}";
+                    await _azureRedis.GetRedisClient(8).HashSetAsync(key, item.studentId, item.ToJsonString());
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 * 24, 0, 0));
+                }
+            }
+        }
+    }
+}

+ 280 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerStudy.cs

@@ -0,0 +1,280 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using DocumentFormat.OpenXml.Bibliography;
+using DocumentFormat.OpenXml.Office2013.Excel;
+using DocumentFormat.OpenXml.Spreadsheet;
+using DocumentFormat.OpenXml.VariantTypes;
+using DocumentFormat.OpenXml.Vml;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Azure.Documents;
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Service;
+using TEAMModelOS.SDK.Models.Service.BI;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public static class TriggerStudy
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+                    CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404)  || tdata.publish == 1)
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                    //await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                          
+                        }
+                        catch (Exception ) {
+                            continue;
+                        }
+                    }
+
+                    await BIStats.SetTypeAddStats(client, _dingDing, tdata.school, "Study", -1, careDate: tdata.startTime);//BI统计增/减量
+                    return;
+                }
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+              
+                Study study = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Study>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+                
+                if (study != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", study.code, "-", study.progress);
+                    var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> changeRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (study.progress)
+                    {
+                        case "pending":
+                            var messageWork = new ServiceBusMessage(new { id = tdata.id, progress = "going", code = tdata.code }.ToJsonString());
+                            messageWork.ApplicationProperties.Add("name", "Study");
+                            if (changeRecords.Count > 0)
+                            {
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                changeRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWork, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageWork.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":
+                            try
+                            {
+                                List<(string pId, List<string> gid)> ps = new List<(string pId, List<string> gid)>();
+                                if (study.groupLists.Count > 0)
+                                {
+                                    var group = study.groupLists;
+                                    foreach (var gp in group)
+                                    {
+                                        foreach (KeyValuePair<string, List<string>> pp in gp)
+                                        {
+                                            ps.Add((pp.Key, pp.Value));
+                                        }
+                                    }
+                                }
+                                (List<RMember> tchList, List<RGroupList> classInfos) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, study.tchLists, study.school, ps);
+                                List<StuActivity> tchActivities = new List<StuActivity>();
+                                (string standard, List<string> tmdids, string school, List<string> update, int statistics) list = (null, null, null, new List<string> { StatisticsService.OfflineRecord }, 0);
+                                if (tchList.IsNotEmpty())
+                                {
+                                    list.tmdids = tchList.Select(x => x.id).ToList();
+                                    School school = null;
+                                    if (!string.IsNullOrEmpty(study.school))
+                                    {
+                                        school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(study.school, new Azure.Cosmos.PartitionKey("Base"));
+                                        list.school = school.id;
+                                        list.standard = school.standard;
+                                    }
+                                    string queryScore = $" select c.id from c where c.id ='{study.id}' and c.pk = 'Activity' ";
+                                    List<string> ids = new();
+                                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryStreamIterator
+                                        (queryText: queryScore))
+                                    {
+                                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                        {
+                                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                            {
+                                                if (obj.TryGetProperty("id", out JsonElement acId))
+                                                {
+                                                    ids.Add(acId.GetString());
+                                                }
+                                            }
+                                        }
+                                    }
+
+
+                                    if (ids.Count == 0)
+                                    {
+                                        tchList.ForEach(x =>
+                                        {
+                                            tchActivities.Add(new StuActivity
+                                            {
+                                                pk = "Activity",
+                                                id = study.id,
+                                                code = $"Activity-{x.id}",
+                                                type = "Study",
+                                                name = study.name,
+                                                startTime = study.startTime,
+                                                endTime = study.endTime,
+                                                scode = study.code,
+                                                scope = study.scope,
+                                                school = study.school,
+                                                creatorId = study.creatorId,
+                                                subjects = new List<string> { "" },
+                                                blob = null,
+                                                owner = study.owner,
+                                                createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                                taskStatus = -1,
+                                                classIds = study.tchLists
+                                            });
+                                        });
+                                    }
+
+                                }
+                                await IESActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
+                                await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
+                                var messageWorkEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                                messageWorkEnd.ApplicationProperties.Add("name", "Study");
+                                if (study.teacIds != null && study.teacIds.Count == 0)
+                                {
+                                    study.teacIds = list.tmdids;
+                                    await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(study, study.id, new PartitionKey($"{study.code}"));
+                                }
+
+                                if (changeRecords.Count > 0)
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    try
+                                    {
+                                        await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                    }
+                                    catch (Exception)
+                                    {
+                                    }
+                                    changeRecords[0].sequenceNumber = end;
+                                    await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                                }
+                                else
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageWorkEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                    ChangeRecord changeRecord = new()
+                                    {
+                                        RowKey = tdata.id,
+                                        PartitionKey = PartitionKey,
+                                        sequenceNumber = end,
+                                        msgId = messageWorkEnd.MessageId
+                                    };
+                                    await table.Save<ChangeRecord>(changeRecord);
+                                }
+
+                            }
+                            catch (Exception ex)
+                            {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修活动异常-going {ex.Message}\n{ex.StackTrace}{tdata.ToJsonString()}{input}", GroupNames.醍摩豆服務運維群組);
+                            }
+                            finally {
+                                string pk = string.Format("{0}{1}{2}", study.code, "-", "pending");
+                                await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            }
+                           
+                            break;
+                        case "finish":
+                            try
+                            {
+                                List<(string pId, List<string> gid)> gls = new List<(string pId, List<string> gid)>();
+                                if (study.groupLists.Count > 0)
+                                {
+                                    var group = study.groupLists;
+                                    foreach (var gp in group)
+                                    {
+                                        foreach (KeyValuePair<string, List<string>> pp in gp)
+                                        {
+                                            gls.Add((pp.Key, pp.Value));
+                                        }
+                                    }
+                                }
+                                //处理教研活动结束统计账户信息
+                                if (study.staffIds.Count == 0)
+                                {
+                                    List<FMember> idList = await GroupListService.GetFinishMemberInfo(_coreAPIHttpService, client, _dingDing, study.school, study.classes, study.stuLists, study.tchLists, gls);
+                                    study.staffIds = idList;
+                                    await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<Study>(study, study.id, new Azure.Cosmos.PartitionKey(study.code));
+
+                                }
+
+                            }
+                            catch (Exception e)
+                            {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修活动异常-finish {e.Message}\n{e.StackTrace}{tdata.ToJsonString()}{input}", GroupNames.醍摩豆服務運維群組);
+                            }
+                            finally {
+                                string pk = string.Format("{0}{1}{2}", study.code, "-", "going");
+                                await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            }
+                            break;
+                    }
+                }
+
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修活动异常{ex.Message}\n{ex.StackTrace}{tdata.ToJsonString()}{input}", GroupNames.醍摩豆服務運維群組);
+            }
+
+        }
+    }
+}

+ 530 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerSurvey.cs

@@ -0,0 +1,530 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using Azure.Storage.Blobs.Models;
+using Azure.Storage.Sas;
+using Microsoft.Azure.Documents;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
+using TEAMModelOS.SDK.Module.AzureBlob.Configuration;
+using TEAMModelOS.SDK.Models.Service;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Extensions.Configuration;
+using System.Linq;
+using TEAMModelOS.SDK.Models.Service.BI;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public class TriggerSurvey
+    {
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService,AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+               CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404) )
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                    //await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Record:{tdata.id}");
+                    _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Submit:{tdata.id}");
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                         
+                        }
+                        catch (Exception)
+                        {
+                            continue;
+                        }
+                    }
+
+                    await BIStats.SetTypeAddStats(client, _dingDing, tdata.school, "Survey", -1, careDate: tdata.startTime);//BI统计增/减量
+                    return;
+                }
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+                Survey survey = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Survey>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+
+                if (survey != null)
+                {
+                    var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    string PartitionKey = string.Format("{0}{1}{2}", survey.code, "-", survey.progress);
+                    List<ChangeRecord> changeRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (survey.progress)
+                    {
+                        case "pending":
+                            var messageSurvey = new ServiceBusMessage(new { tdata.id, progress = "going", tdata.code }.ToJsonString());
+                            messageSurvey.ApplicationProperties.Add("name", "Survey");
+                            if (changeRecords.Count > 0)
+                            {
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageSurvey, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                changeRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageSurvey, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageSurvey.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":                           
+                            List<(string pId, List<string> gid)> ps = new List<(string pId, List<string> gid)>();
+                            if (survey.groupLists.Count > 0)
+                            {
+                                var group = survey.groupLists;
+                                foreach (var gp in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in gp)
+                                    {
+                                        ps.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            List<string> classes = ExamService.getClasses(survey.classes, survey.stuLists);
+                            (List<RMember> tmdIds, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, survey.school);
+                            var addStudentsCls = tmdIds.FindAll(x => x.type == 2);
+                            var addTmdidsCls = tmdIds.FindAll(x => x.type == 1);
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}写入学生表作为活动列表!", GroupNames.醍摩豆服務運維群組);
+#endif
+                            List<StuActivity> stuActivities = new List<StuActivity>();
+                            List<StuActivity> tmdActivities = new List<StuActivity>();
+                            List<StuActivity> tchActivities = new List<StuActivity>();
+                            /*
+                              
+                              List<string> sub = new();
+                              if (survey.tchLists.Count == 0) {
+                                  if (survey.targets.Count > 0)
+                                  {
+                                      foreach (var course in survey.targets)
+                                      {
+                                          var info = course.ToObject<List<string>>();
+                                          if (info.Count > 1)
+                                          {
+                                              sub.Add(info[0]);
+                                          }
+                                      }
+                                  }
+                              }
+
+                              if (addTmdidsCls.IsNotEmpty())
+                              {
+                                  addTmdidsCls.ForEach(x =>
+                                  {
+                                      HashSet<string> classIds = new HashSet<string>();
+                                      classLists.ForEach(z => {
+                                          z.members.ForEach(y => {
+                                              if (y.id.Equals(x.id)&& y.type==1) 
+                                              {
+                                                  classIds.Add(z.id);
+                                              }
+                                          });
+                                      });
+                                      tmdActivities.Add(new StuActivity
+                                      {
+                                          pk = "Activity",
+                                          id = survey.id,
+                                          code = $"Activity-{x.id}",
+                                          type = "Survey",
+                                          name = survey.name,
+                                          startTime = survey.startTime,
+                                          endTime = survey.endTime,
+                                          scode = survey.code,
+                                          scope = survey.scope,
+                                          school = survey.school,
+                                          creatorId = survey.creatorId,
+                                          subjects = sub,
+                                          blob = survey.blob,
+                                          owner = survey.owner,
+                                          isSub = survey.isSub,
+                                          createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                          taskStatus = -1,
+                                          classIds = classIds.ToList()
+                                      });
+                                  });
+                              }
+                              if (addStudentsCls.IsNotEmpty())
+                              {
+                                  addStudentsCls.ForEach(x =>
+                                  {
+                                      HashSet<string> classIds = new HashSet<string>();
+                                      classLists.ForEach(z => {
+                                          z.members.ForEach(y => {
+                                              if (y.id.Equals(x.id)&& y.code.Equals(survey.school) && y.type == 2)
+                                              {
+                                                  classIds.Add(z.id);
+                                              }
+                                          });
+                                      });
+                                      stuActivities.Add(new StuActivity
+                                      {
+                                          pk = "Activity",
+                                          id = survey.id,
+                                          code = $"Activity-{x.code.Replace("Base-", "")}-{x.id}",
+                                          type = "Survey",
+                                          name = survey.name,
+                                          startTime = survey.startTime,
+                                          endTime = survey.endTime,
+                                          scode = survey.code,
+                                          scope = survey.scope,
+                                          school = survey.school,
+                                          creatorId = survey.creatorId,
+                                          subjects = sub,
+                                          blob = survey.blob,
+                                          owner = survey.owner,
+                                          isSub = survey.isSub,
+                                          createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                          taskStatus = -1,
+                                          classIds = classIds.ToList()
+                                      }); 
+                                  });
+                              }
+                             */
+                            (List<RMember> tchList, List<RGroupList> classInfos) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, survey.tchLists, survey.school, ps);
+                            (string standard, List<string> tmdids, string school, List<string> update, int statistics) list = (null, null, null, new List<string> { StatisticsService.TeacherSurvey }, 0);
+                            if (tchList.IsNotEmpty())
+                            {
+                                list.tmdids = tchList.Select(x => x.id).ToList();
+                                School school = null;
+                                if (!string.IsNullOrEmpty(survey.school))
+                                {
+                                    school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(survey.school, new Azure.Cosmos.PartitionKey("Base"));
+                                    list.school = school.id;
+                                    list.standard = school.standard;
+                                }
+
+                                tchList.ForEach(x =>
+                               {
+                                   HashSet<string> classIds = new HashSet<string>();
+                                   classInfos.ForEach(z =>
+                                   {
+                                       z.members.ForEach(y =>
+                                       {
+                                           if (y.id.Equals(x.id) && y.type == 1)
+                                           {
+                                               classIds.Add(z.id);
+                                           }
+                                       });
+                                   });
+                                   tchActivities.Add(new StuActivity
+                                   {
+                                       pk = "Activity",
+                                       id = survey.id,
+                                       code = $"Activity-{x.id}",
+                                       type = "Survey",
+                                       name = survey.name,
+                                       startTime = survey.startTime,
+                                       endTime = survey.endTime,
+                                       scode = survey.code,
+                                       scope = survey.scope,
+                                       school = survey.school,
+                                       creatorId = survey.creatorId,
+                                       subjects = new List<string> { "" },
+                                       blob = survey.blob,
+                                       owner = survey.owner,
+                                       isSub = survey.isSub,
+                                       createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                       taskStatus = -1,
+                                       classIds = classIds.ToList()
+                                   });
+                               });
+
+
+                            }
+                            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+                            await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
+                            //向学生或醍摩豆账号发起通知
+                            #region
+                            //Notice notice = new Notice()
+                            //{
+                            //    creation = survey.startTime,
+                            //    expire = survey.endTime,
+                            //    creatorId = survey.creatorId,
+                            //    stuids = students,
+                            //    tmdids = tmdids,
+                            //    type = "notice",//问卷参加参加通知
+                            //    priority = "normal",
+                            //    //data = new { }.ToJsonString()
+                            //    msgId = survey.id,
+                            //    school = survey.school,
+                            //    scope = survey.scope,
+                            //    body = new Body { sid = survey.id, scode = survey.code, spk = survey.pk, biztype = "survey-join" }
+
+                            //};
+                            //var messageBlob = new ServiceBusMessage(notice.ToJsonString());
+                            //messageBlob.ApplicationProperties.Add("name", "Notice");
+                            //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageBlob);
+                            #endregion
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}写入完成!", GroupNames.醍摩豆服務運維群組);
+#endif                           
+                            var messageSurveyEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                            messageSurveyEnd.ApplicationProperties.Add("name", "Survey");
+                            string pkey = string.Format("{0}{1}{2}", survey.code, "-", "pending");
+                            await table.DeleteSingle<ChangeRecord>(pkey, tdata.id);
+                            if (changeRecords.Count > 0)
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageSurveyEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), changeRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                changeRecords[0].sequenceNumber = end;
+                                await table.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
+                            }
+                            else
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageSurveyEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                ChangeRecord changeRecord = new ChangeRecord
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = end,
+                                    msgId = messageSurveyEnd.MessageId
+                                };
+                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}将于:{tdata.endTime}完成并结算!", GroupNames.醍摩豆服務運維群組);
+#endif
+                            break;
+                        case "finish":
+                            
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}开始结算{tdata.endTime}!", GroupNames.醍摩豆服務運維群組);
+#endif
+                            var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{survey.id}");
+                            var submits = await _azureRedis.GetRedisClient(8).SetMembersAsync($"Survey:Submit:{survey.id}");
+                            List<dynamic> recs = new List<dynamic>();
+                            foreach (var rcd in records)
+                            {
+                                var value = rcd.Value.ToString().ToObject<JsonElement>();
+                                recs.Add(new { index = rcd.Name.ToString(), ans = value });
+                            }
+
+                            List<dynamic> userids = new List<dynamic>();
+                            foreach (var submit in submits)
+                            {
+                                var value = submit.ToString();
+                                userids.Add(value);
+                            }
+
+                            List<QuestionRecord> questionRecords = new List<QuestionRecord>();
+                            //结算每道题的答题情况
+                            var ContainerClient = _azureStorage.GetBlobContainerClient(blobcntr);
+                            List<Task<string>> tasks = new List<Task<string>>();
+                            //获取
+                            List<SurveyRecord> surveyRecords = new List<SurveyRecord>();
+                            try
+                            {
+                                List<string> items = await ContainerClient.List($"survey/{survey.id}/urecord");
+
+                                foreach (string item in items)
+                                {
+                                    var Download = await _azureStorage.GetBlobContainerClient(blobcntr).GetBlobClient(item).DownloadAsync();
+                                    var json = await JsonDocument.ParseAsync(Download.Value.Content);
+                                    var Record = json.RootElement.ToObject<SurveyRecord>();
+                                    surveyRecords.Add(Record);
+                                }
+#if DEBUG
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查问题结算数据{surveyRecords.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+#endif
+                                for (int index = 0; index < survey.answers.Count; index++)
+                                {
+                                    string url = $"{survey.id}/qrecord/{index}.json";
+                                    QuestionRecord question = new QuestionRecord() { index = index };
+                                    foreach (SurveyRecord record in surveyRecords)
+                                    {
+                                        if (record.ans.Count == survey.answers.Count)
+                                        {
+                                            foreach (var an in record.ans[index])
+                                            {
+                                                //
+                                                if (question.opt.ContainsKey(an))
+                                                {
+                                                    if (question.opt[an] != null)
+                                                    {
+                                                        question.opt[an].Add(record.userid);
+                                                    }
+                                                    else
+                                                    {
+                                                        question.opt[an] = new HashSet<string>() { record.userid };
+                                                    }
+                                                }
+                                                else
+                                                {
+                                                    if (survey.answers[index].Contains(an))
+                                                    {
+                                                        //如果是客观题code
+                                                        question.opt.Add(an, new HashSet<string> { record.userid });
+                                                    }
+                                                    else
+                                                    {
+                                                        //如果不是客观code
+                                                        question.other[record.userid] = an;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                    questionRecords.Add(question);
+                                    tasks.Add(_azureStorage.GetBlobContainerClient(blobcntr).UploadFileByContainer(question.ToJsonString(), "survey", url));
+                                }
+                                await Task.WhenAll(tasks);
+                            }
+                            catch (Exception ex)
+                            {
+                                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查问题结算异常{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+                            }
+
+                            List<AnswerRecord> answerRecords = new List<AnswerRecord>();
+                            if (questionRecords.IsNotEmpty())
+                            {
+
+                               
+                                foreach (var questionRecord in questionRecords)
+                                {
+                                    AnswerRecord answerRecord = new AnswerRecord();
+                                    answerRecord.index = questionRecord.index.ToString();
+
+                                    foreach (var opt in questionRecord.opt)
+                                    {
+                                        if (!answerRecord.ans.ContainsKey(opt.Key))
+                                        {
+                                            answerRecord.ans[opt.Key] = 0;
+                                        }
+                                        answerRecord.ans[opt.Key] += opt.Value.Count;
+                                    }
+
+                                    answerRecords.Add(answerRecord);
+                                }
+                                //string resultJson = JsonConvert.SerializeObject(answerRecords, Formatting.Indented);
+                            }
+                            var cods = new { records = answerRecords, userids, question = questionRecords, urecord = surveyRecords };
+                            //问卷整体情况
+                            await _azureStorage.GetBlobContainerClient(blobcntr).UploadFileByContainer(cods.ToJsonString(), "survey", $"{survey.id}/record.json");
+
+                            List<(string pId, List<string> gid)> gls = new List<(string pId, List<string> gid)>();
+                            if (survey.groupLists.Count > 0)
+                            {
+                                var group = survey.groupLists;
+                                foreach (var gp in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in gp)
+                                    {
+                                        gls.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            //处理问卷调查活动结束统计账户信息
+                            List<FMember> idsList = await GroupListService.GetFinishMemberInfo(_coreAPIHttpService, client, _dingDing, survey.school, survey.classes, survey.stuLists, survey.tchLists,gls);
+                            survey.staffIds = idsList;
+
+                            if (string.IsNullOrEmpty(survey.recordUrl))
+                            {
+                                survey.recordUrl = $"/survey/{survey.id}/record.json";
+                                await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync<Survey>(survey, survey.id, new Azure.Cosmos.PartitionKey(survey.code));
+                            }
+                            else
+                            {
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Record:{survey.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Submit:{survey.id}");
+                                break;
+                            }
+                            string pk = string.Format("{0}{1}{2}", survey.code, "-", "going");
+                            await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            //更新结束状态
+                            //data.progress = "finish";
+                            //if (survey.scope .Equals("school"))
+                            //{
+                            //    await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<ActivityData>(data, data.id, new Azure.Cosmos.PartitionKey(data.code));
+                            //}
+                            //else if (survey.scope .Equals("private"))
+                            //{
+                            //    await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<ActivityData>(data, data.id, new Azure.Cosmos.PartitionKey(data.code));
+                            //}
+
+                            break;
+                    }
+                }
+            }
+
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+
+            }
+        }
+    }
+    /**
+     * {survey.id}/qrecord/{index}.json
+        {
+            "opt": {
+                "A": [
+                    "userid1",
+                    "userid2",
+                    "userid3"
+                ],
+                "B": [
+                    "userid1",
+                    "userid2",
+                    "userid3"
+                ]
+            },
+            "other": {
+                "userid1": "建议XXXX1",
+                "userid2": "建议XXXX2"
+            }
+        }
+     **/
+}

+ 422 - 0
TEAMModelOS.Function/CosmosDBTriggers/TriggerVote.cs

@@ -0,0 +1,422 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using Microsoft.Azure.Documents;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
+using TEAMModelOS.SDK.Models.Service;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Extensions.Configuration;
+using DocumentFormat.OpenXml.Office2013.Excel;
+using DocumentFormat.OpenXml.Vml;
+using TEAMModelOS.SDK.Models.Service.BI;
+using TEAMModelOS.Function;
+namespace TEAMModelOS.CosmosDBTriggers
+{
+    public static class TriggerVote
+    {
+
+        public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+            CosmosClient client, JsonElement input, TriggerData tdata, AzureRedisFactory _azureRedis, IConfiguration _configuration)
+        {
+            try
+            {
+                if ((tdata.status != null && tdata.status.Value == 404) )
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
+                    ActivityList data = input.ToObject<ActivityList>();
+                   // await IESActivityService. DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{tdata.id}");
+                    _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{tdata.id}");
+                    var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
+                    foreach (var record in records)
+                    {
+                        try
+                        {
+                            await table_cancel.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                            await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                        }
+                        catch (Exception)
+                        {
+                            continue;
+                        }
+                    }
+
+                    await BIStats.SetTypeAddStats(client, _dingDing, tdata.school, "Vote", -1, careDate: tdata.startTime);//BI统计增/减量
+                    return;
+                }
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                var adid = tdata.id;
+                var adcode = "";
+                string blobcntr = null;
+                if (tdata.scope.Equals("school"))
+                {
+                    adcode = $"Activity-{tdata.school}";
+                    blobcntr = tdata.school;
+                }
+                else
+                {
+                    adcode = $"Activity-{tdata.creatorId}";
+                    blobcntr = tdata.creatorId;
+                }
+               // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在操作", GroupNames.醍摩豆服務運維群組);
+                Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(tdata.id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
+
+                if (vote != null)
+                {
+                    string PartitionKey = string.Format("{0}{1}{2}", vote.code, "-", vote.progress);
+                    List<ChangeRecord> voteRecords = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id }, { "PartitionKey", PartitionKey } });
+                    switch (vote.progress)
+                    {
+                        case "pending":
+                            var messageVote = new ServiceBusMessage(new { id = tdata.id, progress = "going", code = tdata.code }.ToJsonString());
+                            messageVote.ApplicationProperties.Add("name", "Vote");
+                            if (voteRecords.Count > 0)
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                try {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), voteRecords[0].sequenceNumber);
+                                } catch (Exception) { 
+                                }
+                                voteRecords[0].sequenceNumber = start;
+                                await table.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                            }
+                            else
+                            {
+                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                ChangeRecord changeRecord = new ChangeRecord
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = start,
+                                    msgId = messageVote.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+                            break;
+                        case "going":
+
+                            List<(string pId, List<string> gid)> ps = new List<(string pId, List<string> gid)>();
+                            if (vote.groupLists.Count > 0)
+                            {
+                                var group = vote.groupLists;
+                                foreach (var keys in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in keys)
+                                    {
+                                        ps.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            List<string> classes = ExamService.getClasses(vote.classes, vote.stuLists);
+                            (List<RMember> tmdIds, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, vote.school, ps);
+                            var addStudentsCls = tmdIds.FindAll(x => x.type == 2);
+                            var addTmdidsCls = tmdIds.FindAll(x => x.type == 1);
+                            //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动" +
+                            //    $"{tmdids.ToJsonString()}\n" +
+                            //    $"{students.ToJsonString()}\n" +
+                            //    $"{classLists.ToJsonString()}\n" +
+                            //    $"{classes.ToJsonString()}\n", GroupNames.醍摩豆服務運維群組);
+                            List<string> tmds = new List<string>();
+                            if (addTmdidsCls.IsNotEmpty())
+                            {
+                                tmds.AddRange(addTmdidsCls.Select(x => x.id).ToList());
+                            }
+                            List<StuActivity> stuActivities = new();
+                            List<StuActivity> tmdActivities = new();
+                            List<StuActivity> tchActivities = new();
+                            /*
+                           
+                           
+                            List<string> sub = new();
+                            if (vote.tchLists.Count == 0)
+                            {
+                                if (vote.targets.Count > 0)
+                                {
+                                    foreach (var course in vote.targets)
+                                    {
+                                        var info = course.ToObject<List<string>>();
+                                        if (info.Count > 1)
+                                        {
+                                            sub.Add(info[0]);
+                                        }
+                                    }
+                                }
+                            }
+
+                            if (tmds.IsNotEmpty())
+                            {
+                                tmds.ForEach(x =>
+                                {
+                                    HashSet<string> classIds = new HashSet<string>();
+                                    classLists.ForEach(z =>
+                                    {
+                                        z.members.ForEach(y =>
+                                        {
+                                            if (y.id.Equals(x) && y.type == 1)
+                                            {
+                                                classIds.Add(z.id);
+                                            }
+                                        });
+                                    });
+                                    tmdActivities.Add(new StuActivity
+                                    {
+                                        pk = "Activity",
+                                        id = vote.id,
+                                        code = $"Activity-{x}",
+                                        type = "Vote",
+                                        name = vote.name,
+                                        startTime = vote.startTime,
+                                        endTime = vote.endTime,
+                                        scode = vote.code,
+                                        scope = vote.scope,
+                                        school = vote.school,
+                                        creatorId = vote.creatorId,
+                                        subjects = sub,
+                                        blob = null,
+                                        owner = vote.owner,
+                                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                        taskStatus = -1,
+                                        classIds = classIds.ToList()
+                                    });
+                                });
+                            }
+                            if (addStudentsCls.IsNotEmpty())
+                            {
+                                addStudentsCls.ForEach(x =>
+                                {
+                                    HashSet<string> classIds = new HashSet<string>();
+                                    classLists.ForEach(z =>
+                                    {
+                                        z.members.ForEach(y =>
+                                        {
+                                            if (y.id.Equals(x.id) && y.code.Equals(vote.school) && y.type == 2)
+                                            {
+                                                classIds.Add(z.id);
+                                            }
+                                        });
+                                    });
+                                    stuActivities.Add(new StuActivity
+                                    {
+                                        pk = "Activity",
+                                        id = vote.id,
+                                        code = $"Activity-{x.code.Replace("Base-", "")}-{x.id}",
+                                        type = "Vote",
+                                        name = vote.name,
+                                        startTime = vote.startTime,
+                                        endTime = vote.endTime,
+                                        scode = vote.code,
+                                        scope = vote.scope,
+                                        school = vote.school,
+                                        creatorId = vote.creatorId,
+                                        subjects = sub,
+                                        blob = null,
+                                        owner = vote.owner,
+                                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                        taskStatus = -1,
+                                        classIds = classIds.ToList()
+                                    });
+                                });
+                            }
+                             */
+                            (List<RMember> tchList, List<RGroupList> classInfos) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, vote.tchLists, vote.school, ps);
+                            (string standard, List<string> tmdids, string school, List<string> update, int statistics) list = (null, null, null, new List<string> { StatisticsService.TeacherVote }, 0);
+                            if (tchList.IsNotEmpty())
+                            {
+                                list.tmdids = tchList.Select(x => x.id).ToList();
+                                School school = null;
+                                if (!string.IsNullOrEmpty(vote.school))
+                                {
+                                    school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(vote.school, new Azure.Cosmos.PartitionKey("Base"));
+                                    list.school = school.id;
+                                    list.standard = school.standard;
+                                }
+
+                                tchList.ForEach(x =>
+                               {
+                                   HashSet<string> classIds = new HashSet<string>();
+                                   classInfos.ForEach(z =>
+                                   {
+                                       z.members.ForEach(y =>
+                                       {
+                                           if (y.id.Equals(x.id) && y.type == 1)
+                                           {
+                                               classIds.Add(z.id);
+                                           }
+                                       });
+                                   });
+                                   tchActivities.Add(new StuActivity
+                                   {
+                                       pk = "Activity",
+                                       id = vote.id,
+                                       code = $"Activity-{x.id}",
+                                       type = "Vote",
+                                       name = vote.name,
+                                       startTime = vote.startTime,
+                                       endTime = vote.endTime,
+                                       scode = vote.code,
+                                       scope = vote.scope,
+                                       school = vote.school,
+                                       creatorId = vote.creatorId,
+                                       subjects = new List<string> { "" },
+                                       blob = null,
+                                       owner = vote.owner,
+                                       createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                                       taskStatus = -1,
+                                       classIds = classIds.ToList()
+                                   });
+                               });
+
+
+                            }
+                            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+                            await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
+                            //向学生或醍摩豆账号发起通知
+                            #region
+                            //Notice notice = new Notice()
+                            //{
+
+                            //    creation = vote.startTime,
+                            //    expire = vote.endTime,
+                            //    creatorId = vote.creatorId,
+                            //    stuids = students,
+                            //    tmdids = tmdids,
+                            //    type = "notice",//问卷参加参加通知
+                            //    priority = "normal",
+                            //    msgId=vote.id,
+                            //    school = vote.school,
+                            //    scope = vote.scope,
+                            //    //data = new { }.ToJsonString()
+                            //    body = new Body { sid = vote.id, scode = vote.code, spk = vote.pk,  biztype = "vote-join" }
+
+                            //};
+
+                            //var messageBlob = new ServiceBusMessage(notice.ToJsonString());
+                            //messageBlob.ApplicationProperties.Add("name", "Notice");
+                            //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:NoticeTask"), messageBlob);
+                            #endregion
+                            var messageVoteEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
+                            messageVoteEnd.ApplicationProperties.Add("name", "Vote");
+                            string pk = string.Format("{0}{1}{2}", vote.code, "-", "pending");
+                            await table.DeleteSingle<ChangeRecord>(pk, tdata.id);
+                            if (voteRecords.Count > 0)
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                try
+                                {
+                                    await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), voteRecords[0].sequenceNumber);
+                                }
+                                catch (Exception)
+                                {
+                                }
+                                voteRecords[0].sequenceNumber = end;
+                                await table.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                            }
+                            else
+                            {
+                                long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(tdata.endTime));
+                                ChangeRecord changeRecord = new()
+                                {
+                                    RowKey = tdata.id,
+                                    PartitionKey = PartitionKey,
+                                    sequenceNumber = end,
+                                    msgId = messageVoteEnd.MessageId
+                                };                                
+                                await table.Save<ChangeRecord>(changeRecord);
+                            }
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动{tdata.id}将于:{tdata.endTime}完成并结算!", GroupNames.醍摩豆服務運維群組);
+#endif
+                            break;
+                        case "finish":
+                           
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动{tdata.id}开始结算{tdata.endTime}!", GroupNames.醍摩豆服務運維群組);
+#endif
+                            //获取投票活动的所有投票记录
+                            string pkey = string.Format("{0}{1}{2}", vote.code, "-", "going");
+                            await table.DeleteSingle<ChangeRecord>(pkey, tdata.id);
+                            var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{vote.id}");
+                            //获取投票活动的选项及投票数
+                            var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{vote.id}");
+                            List<dynamic> countcds = new List<dynamic>();
+                            if (counts != null && counts.Length > 0)
+                            {
+                                foreach (var count in counts)
+                                {
+                                    countcds.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                                }
+                            }
+                            List<Task<string>> tasks = new();
+                            List<VoteRecord> recordsBlob = new();
+                            foreach (var rcd in records)
+                            {
+                                var value = rcd.Value.ToString().ToObject<VoteRecord>();
+                                recordsBlob.Add(value);
+                            }
+                            //分组每个人的 
+                            var gp = recordsBlob.GroupBy(x => x.userid).Select(x => new { key = x.Key, list = x.ToList() });
+                            foreach (var g in gp)
+                            {
+                                tasks.Add(_azureStorage.GetBlobContainerClient(blobcntr).UploadFileByContainer(g.list.ToJsonString(), "vote", $"{vote.id}/urecord/{g.key}.json"));
+                            }
+                            //处理活动方的记录, 
+                            string url = $"/vote/{vote.id}/record.json";
+                            tasks.Add(_azureStorage.GetBlobContainerClient(blobcntr).UploadFileByContainer(new { options = countcds, records = recordsBlob }.ToJsonString(), "vote", $"{vote.id}/record.json"));
+                            //处理投票者的记录
+                            List<(string pId, List<string> gid)> gls = new List<(string pId, List<string> gid)>();
+                            if (vote.groupLists.Count > 0)
+                            {
+                                var group = vote.groupLists;
+                                foreach (var gro in group)
+                                {
+                                    foreach (KeyValuePair<string, List<string>> pp in gro)
+                                    {
+                                        gls.Add((pp.Key, pp.Value));
+                                    }
+                                }
+                            }
+                            //处理投票活动结束统计账户信息
+                            List<FMember> idsList = await GroupListService.GetFinishMemberInfo(_coreAPIHttpService, client, _dingDing, vote.school, vote.classes, vote.stuLists, vote.tchLists, gls);
+                            vote.staffIds = idsList;
+
+                            if (string.IsNullOrEmpty(vote.recordUrl))
+                            {
+                                vote.recordUrl = url;
+                                await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<Vote>(vote, vote.id, new Azure.Cosmos.PartitionKey(vote.code));
+                            }
+                            else
+                            {
+                                //异动,且已经有结算记录则不必再继续。
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}");
+                                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}");
+                                break;
+                            }
+                            await Task.WhenAll(tasks);                         
+                            break;
+                    }
+                }
+            }
+            catch (CosmosException e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDB异常{e.Message}\n{e.StackTrace}\n{e.Status}", GroupNames.醍摩豆服務運維群組);
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动异常{ex.Message}\n{ex.StackTrace}\n{input.ToJsonString()}\n{tdata.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+            }
+
+        }
+    }
+}

+ 51 - 0
TEAMModelOS.Function/DI/BackgroundWorkerQueue.cs

@@ -0,0 +1,51 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.Function.DI
+{
+    public class BackgroundWorkerQueue
+    {
+        private readonly ConcurrentQueue<Func<CancellationToken, Task>> _workItems = new();
+        private readonly SemaphoreSlim _signal = new(0);
+
+        public async Task<Func<CancellationToken, Task>> DequeueAsync(CancellationToken cancellationToken)
+        {
+            await _signal.WaitAsync(cancellationToken);
+            _workItems.TryDequeue(out var workItem);
+
+            return workItem;
+        }
+
+        public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
+        {
+            ArgumentNullException.ThrowIfNull(workItem);
+
+            _workItems.Enqueue(workItem);
+            _signal.Release();
+        }
+    }
+    public class LongRunningService : BackgroundService
+    {
+
+        public LongRunningService(BackgroundWorkerQueue queue)
+        {
+            _queue = queue;
+        }
+        private readonly BackgroundWorkerQueue _queue;
+
+        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+        {
+            while (!stoppingToken.IsCancellationRequested)
+            {
+                var workItem = await _queue.DequeueAsync(stoppingToken);
+
+                await workItem(stoppingToken);
+            }
+        }
+    }
+}

+ 135 - 0
TEAMModelOS.Function/IESCosmosDBTrigger.cs

@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using System.Xml.Linq;
+using Microsoft.Azure.Cosmos.Linq;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.CosmosDBTriggers;
+namespace TEAMModelOS.Function
+{
+    public class IESCosmosDBTrigger
+    {
+        private readonly ILogger _logger;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly DingDing _dingDing;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly IHttpClientFactory _httpClient;
+        private IConfiguration _configuration { get; set; }
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly HttpTrigger _httpTrigger;
+
+        public IESCosmosDBTrigger(ILoggerFactory loggerFactory, CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureServiceBusFactory azureServiceBus, AzureStorageFactory azureStorage, DingDing dingDing, AzureRedisFactory azureRedis
+          , IConfiguration configuration, IHttpClientFactory httpClient, HttpTrigger httpTrigger)
+        {
+            _logger = loggerFactory.CreateLogger<IESCosmosDBTrigger>();
+            _azureCosmos = azureCosmos;
+            _serviceBus = azureServiceBus;
+            _azureStorage = azureStorage;
+            _dingDing = dingDing;
+            _azureRedis = azureRedis;
+            _configuration = configuration;
+            _coreAPIHttpService=coreAPIHttpService;
+            _httpClient = httpClient;
+            _httpTrigger = httpTrigger;
+        }
+
+        [Function("Common")]
+        public async Task Common([CosmosDBTrigger(
+            databaseName: "TEAMModelOS",
+            containerName: "Common",
+            Connection = "Azure:Cosmos:ConnectionString",
+            LeaseContainerName = "leases",
+            CreateLeaseContainerIfNotExists = true)] IReadOnlyList<JsonElement> input)
+        {
+            if (input != null && input.Count > 0)
+            {
+                _logger.LogInformation("Documents modified: " + input.Count);
+                _logger.LogInformation("First document Id: " +JsonSerializer.Serialize(input[0]));
+                var client = _azureCosmos.GetCosmosClient();
+                foreach (var element in input) {
+                    _logger.LogInformation("变化参数 " + element);
+                    element.TryGetProperty("pk", out JsonElement jsond);
+                    if (!string.IsNullOrWhiteSpace($"{jsond}"))
+                    {
+                        if ($"{jsond}".Equals("Receiver", StringComparison.OrdinalIgnoreCase)
+                            || $"{jsond}".Equals("Notice", StringComparison.OrdinalIgnoreCase)
+                            || $"{jsond}".Equals("ExamClassResult", StringComparison.OrdinalIgnoreCase))
+                        {
+                            ///通知接收者的变更
+                            continue;
+                        }
+                        //else if ($"{jsond}".Equals("StatsNotice", StringComparison.OrdinalIgnoreCase)) 
+                        //{
+                        //    var sert = 012;
+                        //}
+                        else
+                        {
+                            TriggerData data = element.ToObject<TriggerData>();
+                            ///活动类型的变更
+#if DEBUG
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDBTrigger,{data.pk}触发变更\n{data.ToJsonString()}",
+                                            GroupNames.醍摩豆服務運維群組);
+#endif
+                            switch (data.pk)
+                            {
+                                case "Exam":
+                                    await TriggerExam.Trigger(_coreAPIHttpService, _azureCosmos, _serviceBus, _azureStorage, _dingDing, client, element, data, _httpClient, _configuration, _httpTrigger, _azureRedis);
+                                    break;
+                                case "Vote":
+                                    await TriggerVote.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);
+                                    break;
+                                case "Survey":
+                                    await TriggerSurvey.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);
+                                    break;
+                                case "Correct":
+                                    await TriggerCorrect.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis);
+                                    break;
+                                case "ExamLite":
+                                    await TriggerExamLite.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);
+                                    break;
+                                case "Study":
+                                    await TriggerStudy.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);
+                                    break;
+                                case "Homework":
+                                    await TriggerHomework.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);
+                                    break;
+                                case "Art":
+                                    await TriggerArt.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration, _httpTrigger);
+                                    break;
+                                case "ExamImport":
+                                    await TriggerExamImport.Trigger(_coreAPIHttpService, _azureCosmos, _serviceBus, _azureStorage, _dingDing, client, element, data, _httpClient, _configuration, _azureRedis);
+                                    break;
+                                case "QuotaImport":
+                                    await TriggerQuotaImport.Trigger(_coreAPIHttpService, _azureCosmos, _serviceBus, _azureStorage, _dingDing, client, element, data, _httpClient, _configuration, _azureRedis);
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    public class TriggerData
+    {
+        public string pk { get; set; }
+        public int? status { get; set; }
+        public string name { get; set; }
+        public long startTime { get; set; }
+        public long endTime { get; set; }
+        public string school { get; set; }
+        public string code { get; set; }
+        public string creatorId { get; set; }
+        public string progress { get; set; }
+        public string scope { get; set; }
+        public int ttl { get; set; }
+        public string id { get; set; }
+        public int? publish { get; set; }
+    }
+}

+ 986 - 0
TEAMModelOS.Function/IESHttpTrigger.cs

@@ -0,0 +1,986 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using TEAMModelOS.Function.DI;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using TEAMModelOS.Models;
+using Microsoft.Extensions.Options;
+using System.Net;
+using System.Text.Json;
+using System.Reflection;
+using HTEXLib.COMM.Helpers;
+using StackExchange.Redis;
+using static TEAMModelOS.SDK.Services.BlobService;
+using TEAMModelOS.SDK.Models;
+using Azure.Cosmos;
+using TEAMModelOS.SDK.Extension;
+using System.Dynamic;
+using Azure.Storage.Blobs.Models;
+using TEAMModelOS.SDK.Models.Table;
+
+namespace TEAMModelOS.Function
+{
+    public class IESHttpTrigger
+    {
+        private readonly ILogger<IESHttpTrigger> _logger;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly IHttpClientFactory _httpClient;
+        private readonly Option? _option;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IConfiguration _configuration;
+        private readonly BackgroundWorkerQueue _backgroundWorkerQueue;
+        public IESHttpTrigger(ILogger<IESHttpTrigger> logger, AzureCosmosFactory azureCosmos, DingDing dingDing, CoreAPIHttpService coreAPIHttpService, AzureStorageFactory azureStorage , AzureRedisFactory azureRedis, IHttpClientFactory httpClient, IOptionsSnapshot<Option> option,
+             IConfiguration configuration, BackgroundWorkerQueue backgroundWorkerQueue)
+        {
+            _logger = logger;
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
+            _httpClient = httpClient;
+            _coreAPIHttpService = coreAPIHttpService;
+            _option = option?.Value;
+            _configuration = configuration;
+            _backgroundWorkerQueue = backgroundWorkerQueue;
+        }
+
+        [Function("upsert-student-portrait")]
+        public async  Task<IActionResult> UpsertStudentPortrait([HttpTrigger(AuthorizationLevel.Anonymous,  "post",Route =null )] HttpRequest req)
+        {
+            _logger.LogInformation("C# HTTP trigger function processed a request.");
+
+            string data = await new StreamReader(req.Body).ReadToEndAsync();
+            var json = JsonDocument.Parse(data).RootElement;
+            //var response = req.CreateResponse(HttpStatusCode.OK);
+            var responseData = await OpenApiService.UpsertStudentPortrait(_azureCosmos, _dingDing, _azureRedis, json);
+            //  await response.WriteAsJsonAsync(new { data = responseData });
+            // return new OkObjectResult("Welcome to Azure Functions!");
+            return new OkObjectResult(new { data = responseData });
+        }
+
+
+        [Function("system-info-function")]
+        public async Task<IActionResult> SystemInfo([HttpTrigger(AuthorizationLevel.Anonymous,  "post", Route = null)] HttpRequest req)
+        {
+            _logger.LogInformation("C# HTTP trigger function processed a request.");
+
+            Type attr = this.GetType();
+            string currentDirectory = Path.GetDirectoryName(attr.Assembly.Location);
+            Assembly assembly = Assembly.LoadFrom($"{currentDirectory}\\TEAMModelOS.FunctionV4.dll");
+            var description = assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
+            //var v1 = Assembly.GetEntryAssembly().GetName().Version;
+            //var v2 = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
+            // var description = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
+            var version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
+            long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+            //Console.WriteLine($"Assembly.GetEntryAssembly().GetName().Version: " +
+
+            //                  $"{Assembly.GetEntryAssembly().GetName().Version}");5.2107.12.1
+
+            //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version:" +
+            //                  $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version}");5.2107.12.1
+
+            //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion:" +
+
+            //                  $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion}");5.2107.12
+            return new OkObjectResult(new { version, description, nowtime });
+        }
+        [Function("surplus-space-notify")]
+        public   IActionResult SurplusSpaceNotify([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+            string msg = "";
+            _backgroundWorkerQueue.QueueBackgroundWorkItem(async token =>
+            {
+                try
+                {
+                    string data = await new StreamReader(req.Body).ReadToEndAsync();
+                    var json = JsonDocument.Parse(data).RootElement;
+                    json.TryGetProperty("name", out JsonElement _name);
+                    json.TryGetProperty("scope", out JsonElement _scope);
+                    json.TryGetProperty("percent", out JsonElement _percent);
+                    double percent = _percent.GetDouble();
+                    string name = _name.ToString();
+                    string scope = _scope.ToString();
+
+                    int tag = 11;
+                    if (percent <= 10 && percent > 5)
+                    {
+                        tag = 10;
+                    }
+                    else if (percent <= 5 && percent > 0)
+                    {
+                        tag = 5;
+                    }
+                    else if (percent <= 0)
+                    {
+                        tag = 0;
+                    }
+
+                    List<IdNameCode> ids = new List<IdNameCode>();
+                    Teacher teacher = null;
+                    School school = null;
+
+                    if (scope.Equals("school", StringComparison.OrdinalIgnoreCase))
+                    {
+                        school = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(name, new PartitionKey("Base"));
+                        string sql = $"select   value c from c    where c.code='Teacher-{name}' and c.status='join'  and  array_contains(c.roles,'admin') ";
+                        List<SchoolTeacher> adminTeachers = new List<SchoolTeacher>();
+                        await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                            .GetItemQueryIterator<SchoolTeacher>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Teacher-{name}") }))
+                        {
+                            adminTeachers.Add(item);
+                        }
+                        if (adminTeachers.IsNotEmpty())
+                        {
+
+                            string sqlAdmin = $"select c.id,c.lang  as code ,c.name from c where c.id in ({string.Join(",", adminTeachers.Select(z => $"'{z.id}'"))}) ";
+                            await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                                .GetItemQueryIterator<IdNameCode>(queryText: sqlAdmin, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
+                            {
+                                ids.Add(item);
+                            }
+
+                        }
+                    }
+                    else
+                    {
+                        teacher=  await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(name, new PartitionKey("Base"));
+                        ids.Add(new IdNameCode
+                        {
+                            id = teacher.id,
+                            name = teacher.name,
+                            code = teacher.lang
+                        });
+                    }
+                    //如果已经扩容请忽略此通知!
+                    string key = scope.Equals("school", StringComparison.OrdinalIgnoreCase) ? $"Blob:Space:School:Notify:{name}" : $"Blob:Space:Private:Notify:{name}";
+                    foreach (var idnamecode in ids)
+                    {
+                        string filed = $"{idnamecode.id}-{tag}";
+                        BlobSpaceNotify? blobSpaceNotify = null;
+                        RedisValue value = await _azureRedis.GetRedisClient(8).HashGetAsync(key, filed);
+                        if (value != default && !value.IsNullOrEmpty)
+                        {
+                            blobSpaceNotify = value.ToString().ToObject<BlobSpaceNotify>();
+                        }
+                        if (tag < 11)
+                        {
+
+                            if (blobSpaceNotify == null)
+                            {
+                                if ("school".Equals(scope, StringComparison.OrdinalIgnoreCase))
+                                {
+                                    _coreAPIHttpService.PushNotify(new List<IdNameCode> { idnamecode }, $"blob-space-school-notify", Constant.NotifyType_IES5_Management,
+                                    new Dictionary<string, object> { { "tmdname", idnamecode.name }, { "schoolName", school.name }, { "percent", $"{tag}" }, { "schoolId", $"{school.id}" }, { "tmdid", idnamecode.id } },
+                                    $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration, _dingDing, "");
+
+                                }
+                                else
+                                {
+                                    _coreAPIHttpService.PushNotify(new List<IdNameCode> { idnamecode }, $"blob-space-private-notify", Constant.NotifyType_IES5_Management,
+                                        new Dictionary<string, object> { { "tmdname", idnamecode.name }, { "percent", $"{tag}" }, { "tmdid", idnamecode.id } },
+                                        $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration, _dingDing, "");
+                                }
+                                blobSpaceNotify = new BlobSpaceNotify { id = idnamecode.id, tag = tag, containerName = name, scope = scope, notifyIndex = Guid.NewGuid().ToString() };
+                                await _azureRedis.GetRedisClient(8).HashSetAsync(key, filed, blobSpaceNotify.ToJsonString());
+                                await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(hours: 7*24, minutes: 0, seconds: 0));
+                            }
+                            else
+                            {
+                                //已经发送过的不在提交
+                            }
+                        }
+                        else
+                        {
+                            if (blobSpaceNotify != null)
+                            {
+                                //撤销
+                                var index = blobSpaceNotify.notifyIndex;
+                                await _azureRedis.GetRedisClient(8).HashDeleteAsync(key, filed);
+                                _coreAPIHttpService.CancelNotify(index, $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration);
+                            }
+
+                        }
+                    }
+
+                }
+                catch (Exception ex)
+                {
+                    await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},空间不足,通知发送处理异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                }
+
+
+            });
+            return new OkObjectResult(new { msg });
+        }
+
+        [Function("area-artsetting-change")]
+        public async Task<IActionResult> AreaArtSettingChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+            string msg = "";
+            try
+            {
+                string data = await new StreamReader(req.Body).ReadToEndAsync();
+                var json = JsonDocument.Parse(data).RootElement;
+                json.TryGetProperty("areaId", out JsonElement _areaId);
+                string schoolSQL = $"select value c from c where c.areaId='{_areaId}'";
+                List<School> schools = new List<School>();
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                               .GetItemQueryIterator<School>(queryText: schoolSQL, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
+                {
+                    schools.Add(item);
+                }
+                ArtSetting artSetting = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemAsync<ArtSetting>($"{_areaId}", new PartitionKey("ArtSetting"));
+                foreach (var school in schools)
+                {
+                    msg = school.ToJsonString();
+                    List<Period> periods = new List<Period>();
+                    var hastype_period = school.period.Where(p => !string.IsNullOrWhiteSpace(p.periodType));
+                    if (hastype_period.Any())
+                    {
+                        periods.AddRange(hastype_period);
+                    }
+                    var nottype_period = school.period.Where(p => string.IsNullOrWhiteSpace(p.periodType));
+                    if (nottype_period!=null && nottype_period.Count()>0)
+                    {
+                        foreach (var period in nottype_period)
+                        {
+                            if (period.name.Contains("小学"))
+                            {
+                                period.periodType= "primary";
+                            }
+                            if (period.name.Contains("初中"))
+                            {
+                                period.periodType = "junior";
+                            }
+                            if (period.name.Contains("高中"))
+                            {
+                                period.periodType = "senior";
+                            }
+                            if (string.IsNullOrWhiteSpace(period.periodType) && school.period.Count == 1)
+                            {
+                                if (school.name.Contains("小学"))
+                                {
+                                    period.periodType = "primary";
+
+                                }
+                                if (school.name.Contains("初中"))
+                                {
+                                    period.periodType = "junior";
+                                }
+                                if (school.name.Contains("高中"))
+                                {
+                                    period.periodType = "senior";
+                                }
+                            }
+                            if (!string.IsNullOrWhiteSpace(period.periodType))
+                            {
+                                periods.Add(period);
+                            }
+                        }
+                    }
+                    foreach (var period in periods)
+                    {
+                        var dimension = artSetting.dimensions.FindAll(x => x.type.Intersect(new List<string> { period.periodType }).Any());
+                        var bindIds = period.subjects.Where(s => !string.IsNullOrWhiteSpace(s.bindId)).Select(x => x.bindId);
+                        //该学段未同步学科的。
+                        var unBindIds = dimension.Where(z => !string.IsNullOrWhiteSpace(z.subjectBind)).Select(x => x.subjectBind).ToHashSet().Except(bindIds);
+                        if (unBindIds.Any()  && unBindIds.Count()>0)
+                        {
+                            //尝试寻找同名学科且没有设置bindId的
+                            foreach (var unBindId in unBindIds)
+                            {
+                                var subjects = artSetting.dimensions.FindAll(d => !string.IsNullOrWhiteSpace(d.subjectBind) && !string.IsNullOrWhiteSpace(d.subject) && d.subjectBind.Equals(unBindId))?.Select(m => m.subject);
+                                if (subjects != null)
+                                {
+                                    foreach (var subject in subjects)
+                                    {
+                                        //获取同名学科,且没绑定的
+                                        var sub = period.subjects.FindAll(sub => sub.name.Contains(subject) && string.IsNullOrWhiteSpace(sub.bindId));
+                                        if (sub.IsNotEmpty())
+                                        {
+                                            sub[0].bindId = unBindId;
+                                        }
+                                        else
+                                        {
+                                            period.subjects.Add(new Subject { id = Guid.NewGuid().ToString(), name = subject, bindId = unBindId, type = 1 });
+
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        var period_subjects = period.subjects.Where(s => !string.IsNullOrWhiteSpace(s.bindId));
+                        foreach (var subject in period_subjects)
+                        {
+                            var dim = dimension.Where(x => x.subjectBind.Equals(subject.bindId));
+                            if (dim.Any())
+                            {
+                                Knowledge old = null;
+                                string sql = $"select value(c) from c where c.periodId = '{period.id}'";
+                                string pk = $"Knowledge-{school.id}-{subject.id}";
+                                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").
+                                    GetItemQueryIterator<Knowledge>(queryText: sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
+                                {
+                                    old = item;
+                                    break;
+                                }
+
+                                //同步知识块。
+
+                                if (old != null)
+                                {
+                                    bool change = false;
+                                    //如果之前的是1 来源于区级,后面因区级删除,应该还原为0。
+                                    var oldBlocks = old.blocks.Select(x => x.name).ToHashSet();
+                                    var dimBlocks = dim.SelectMany(d => d.blocks);
+                                    //增加的
+                                    var addBlocks = dimBlocks.Except(oldBlocks);
+                                    //减少的
+                                    var cutBlocks = oldBlocks.Except(dimBlocks);
+                                    foreach (var add in addBlocks)
+                                    {
+                                        old.blocks.Add(new Block { name = add, source = 1 });
+                                        change = true;
+                                    }
+                                    //减少的还原为0
+                                    if (cutBlocks.Any())
+                                    {
+                                        old.blocks.ForEach(ob => {
+                                            if (cutBlocks.Contains(ob.name))
+                                            {
+                                                ob.source = 0;
+                                                change = true;
+                                            }
+                                        });
+                                    }
+                                    foreach (var db in dimBlocks)
+                                    {
+                                        old.blocks.ForEach(ob => {
+                                            if (db.Equals(ob.name))
+                                            {
+                                                ob.source = 1;
+                                                change = true;
+                                            }
+                                        });
+                                    }
+                                    if (change)
+                                    {
+                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(old, old.id, new PartitionKey(old.code));
+                                    }
+                                    var count = new { pcount = old.points != null ? old.points.Count : 0, bcount = old.blocks != null ? old.blocks.Count : 0 };
+                                    //处理知识点,知识块计数问题
+                                    await _azureRedis.GetRedisClient(8).HashSetAsync($"Knowledge:Count:{old.owner}-{old.subjectId}", old.periodId, count.ToJsonString());
+                                }
+                                else
+                                {
+                                    var blocks = dim.SelectMany(x => x.blocks).Select(bs => new Block { name = bs, source = 1 });
+                                    if (blocks.Any())
+                                    {
+                                        var _new = new Knowledge
+                                        {
+                                            id = Guid.NewGuid().ToString(),
+                                            pk = "Knowledge",
+                                            code = pk,
+                                            owner = school.id,
+                                            periodId = period.id,
+                                            subjectId = subject.id,
+                                            blocks = blocks.ToList()
+                                        };
+                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).CreateItemAsync(_new, new PartitionKey(_new.code));
+                                        var count = new { pcount = _new.points != null ? _new.points.Count : 0, bcount = _new.blocks != null ? _new.blocks.Count : 0 };
+                                        //处理知识点,知识块计数问题
+                                        await _azureRedis.GetRedisClient(8).HashSetAsync($"Knowledge:Count:{_new.owner}-{_new.subjectId}", _new.periodId, count.ToJsonString());
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(school, school.id, new PartitionKey(school.code));
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},area-artsetting-change,{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
+            }
+            return new OkObjectResult(new { });
+        }
+        [Function("graduate-change")]
+        public async Task<IActionResult> GraduateChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+            dynamic jsondata = new ExpandoObject();
+            try
+            {
+                string data = await new StreamReader(req.Body).ReadToEndAsync();
+                var json = JsonDocument.Parse(data).RootElement;
+                jsondata = json;
+                //await  _dingDing.SendBotMsg( "毕业状态变更:"+json.ToJsonString(), GroupNames.成都开发測試群組);
+                string schoolId = null;
+                if (json.TryGetProperty("schoolId", out JsonElement _schoolId))
+                {
+                    schoolId = $"{_schoolId}";
+                }
+                if (string.IsNullOrEmpty(schoolId))
+                {
+                    return new OkObjectResult(new { });
+                }
+                //计算毕业的
+                if (json.TryGetProperty("graduate_classes", out JsonElement _graduate_classes))
+                {
+                    List<Class> graduate_classes = _graduate_classes.ToObject<List<Class>>();
+                    if (graduate_classes.IsNotEmpty())
+                    {
+                        var ids = graduate_classes.Where(x => !string.IsNullOrWhiteSpace(x.id)).Select(x => $"'{x.id}'");
+                        List<Student> students = new List<Student>();
+                        string sql = $"select value c from c where  (c.graduate = 0 or  IS_DEFINED(c.graduate) = false)  and  c.classId in ({string.Join(",", ids)})";
+                        await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student)
+                            .GetItemQueryIterator<Student>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
+                        {
+                            item.graduate = 1;
+                            students.Add(item);
+                        }
+                        foreach (var item in students)
+                        {
+                            item.graduate = 1;
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync<Student>(item, item.id, new PartitionKey($"Base-{schoolId}"));
+                        }
+                        foreach (var item in graduate_classes)
+                        {
+                            item.graduate = 1;
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync<Class>(item, item.id, new PartitionKey($"Class-{schoolId}"));
+                        }
+                    }
+                }
+                //未毕业的
+                if (json.TryGetProperty("cancel_graduate_classes", out JsonElement _cancel_graduate_classes))
+                {
+                    List<Class> cancel_graduate_classes = _cancel_graduate_classes.ToObject<List<Class>>();
+                    if (cancel_graduate_classes.IsNotEmpty())
+                    {
+                        var ids = cancel_graduate_classes.Where(x => !string.IsNullOrWhiteSpace(x.id)).Select(x => $"'{x.id}'");
+                        List<Student> students = new List<Student>();
+                        string sql = $"select value c from c where  c.graduate =1   and  c.classId in ({string.Join(",", ids)})";
+                        await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student)
+                            .GetItemQueryIterator<Student>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
+                        {
+                            item.graduate = 0;
+                            students.Add(item);
+                        }
+                        foreach (var item in students)
+                        {
+                            item.graduate = 0;
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync<Student>(item, item.id, new PartitionKey($"Base-{schoolId}"));
+                        }
+                        foreach (var item in cancel_graduate_classes)
+                        {
+                            item.graduate = 0;
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync<Class>(item, item.id, new PartitionKey($"Class-{schoolId}"));
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"graduate-change,{ex.Message}\n{ex.StackTrace}\n{jsondata.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+            }
+            return new OkObjectResult(new {   });
+        }
+
+        [Function("lesson-tag-change")]
+        public async Task<IActionResult> LessonTagChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+            string data = await new StreamReader(req.Body).ReadToEndAsync();
+            var json = JsonDocument.Parse(data).RootElement;
+            List<TagOldNew> old_new = null;
+            string school = null;
+            if (json.TryGetProperty("school", out JsonElement _school))
+            {
+                school = _school.GetString();
+            }
+            if (json.TryGetProperty("old_new", out JsonElement _old_new))
+            {
+                old_new = _old_new.ToObject<List<TagOldNew>>();
+            }
+            if (old_new.IsNotEmpty() && !string.IsNullOrWhiteSpace(school))
+            {
+                foreach (var on in old_new)
+                {
+                    List<LessonRecord> lessonRecords = new List<LessonRecord>();
+                    string sql = $"select value(c) from c    where array_contains(c.category,'{on._old}') ";
+                    await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<LessonRecord>
+                        (queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"LessonRecord-{_school}") }))
+                    {
+                        lessonRecords.Add(item);
+                    }
+
+                    lessonRecords.ForEach(item =>
+                    {
+                        //修改标签
+                        if (!string.IsNullOrWhiteSpace(on._new))
+                        {
+                            for (int i = 0; i < item.category.Count; i++)
+                            {
+                                if (item.category[i].Equals(on._old))
+                                {
+                                    item.category[i] = on._new;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            //表示删除标签
+                            item.category.RemoveAll(x => x.Equals(on._old));
+                        }
+                    });
+                    foreach (var item in lessonRecords)
+                    {
+                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey(item.code));
+                    }
+                }
+            }
+            return new OkObjectResult(new { data= json});
+        }
+
+        [Function("knowledge-change")]
+        public async Task<IActionResult> KnowledgeChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+            string data = await new StreamReader(req.Body).ReadToEndAsync();
+            var json = JsonDocument.Parse(data).RootElement;
+            List<TagOldNew> old_new = null;
+            string school = null;
+            if (json.TryGetProperty("school", out JsonElement _school))
+            {
+                school = _school.GetString();
+            }
+            if (json.TryGetProperty("old_new", out JsonElement _old_new))
+            {
+                old_new = _old_new.ToObject<List<TagOldNew>>();
+            }
+            if (old_new.IsNotEmpty() && !string.IsNullOrWhiteSpace(school))
+            {
+                foreach (var on in old_new)
+                {
+                    List<ItemInfo> items = new List<ItemInfo>();
+                    string sql = $"select value(c) from c    where array_contains(c.knowledge,'{on._old}') ";
+                    await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>
+                        (queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{_school}") }))
+                    {
+                        items.Add(item);
+                    }
+
+                    items.ForEach(item =>
+                    {
+                        //修改知识点
+                        if (!string.IsNullOrEmpty(on._new))
+                        {
+                            for (int i = 0; i < item.knowledge.Count; i++)
+                            {
+                                if (item.knowledge[i].Equals(on._old))
+                                {
+                                    item.knowledge[i] = on._new;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            //表示删除知识点
+                            item.knowledge.RemoveAll(x => x.Equals(on._old));
+                        }
+                    });
+                    foreach (var item in items)
+                    {
+                        ItemBlob itemBlob = null;
+                        try
+                        {
+                            BlobDownloadInfo blobDownloadResult = await _azureStorage.GetBlobContainerClient($"{school}").GetBlobClient($"/item/{item.id}/{item.id}.json").DownloadAsync();
+                            if (blobDownloadResult != null)
+                            {
+                                var blob = JsonDocument.Parse(blobDownloadResult.Content);
+                                itemBlob = blob.RootElement.ToObject<ItemBlob>();
+                                itemBlob.exercise.knowledge = item.knowledge;
+                                await _azureStorage.GetBlobContainerClient($"{school}").UploadFileByContainer(itemBlob.ToJsonString(), "item", $"{item.id}/{item.id}.json", true);
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+                            itemBlob = null;
+                        }
+                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey(item.code));
+                    }
+                }
+            }
+            return new OkObjectResult(new {data=json });
+        }
+        [Function("online-record")]
+        public async Task<IActionResult> OnlineRecord([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
+        {
+           
+           // return new OkObjectResult(new { });
+            string data = await new StreamReader(req.Body).ReadToEndAsync();
+            var json = JsonDocument.Parse(data).RootElement;
+            try
+            {
+                string school = null;
+                string scope = null;
+                string id = null;
+                string ip = null;
+                int expire = 1;
+
+                if (json.TryGetProperty("school", out JsonElement _school))
+                    school = _school.GetString();
+
+                if (json.TryGetProperty("scope", out JsonElement _scope))
+                    scope = _scope.GetString();
+
+                if (json.TryGetProperty("id", out JsonElement _id))
+                    id = _id.GetString();
+
+                if (json.TryGetProperty("ip", out JsonElement _ip))
+                    ip = _ip.GetString();
+                if (json.TryGetProperty("expire", out JsonElement _expire))
+                    expire = _expire.GetInt32();
+
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
+                var cosmosClient = _azureCosmos.GetCosmosClient();
+
+                DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+                var dateHour = dateTime.ToString("yyyyMMddHH"); //获取当天的小时
+                var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
+                var dateMonth = dateTime.ToString("yyyyMM");//获取当月的日期
+                var currentHour = dateTime.Hour;   //当前小时
+                var currentDay = dateTime.Day;   //当前天
+                long Expire = dateTime.AddHours(expire).ToUnixTimeMilliseconds();  //token到期时间
+                long now = dateTime.ToUnixTimeMilliseconds();   //当前时间戳
+                DateTime hour = DateTime.UtcNow.AddHours(25);   //25小时到期
+                DateTime month = DateTime.UtcNow.AddDays(32);   //一个月到期
+                var delTbHour = dateTime.AddHours(-168).ToString("yyyyMMddHH");   //168小时前
+                var delTbDay = dateTime.AddDays(-180).ToString("yyyyMMdd");   //180天前            
+                switch (scope)
+                {
+                    case "teacher":
+                        try
+                        {
+                            //Teacher teacher = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
+                            //teacher.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
+                            //await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+                        }
+                        catch { }
+                        break;
+                    case "student":
+                        try
+                        {
+                            // Student student = await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReadItemAsync<Student>(id, new PartitionKey($"Base-{school}"));
+                            //student.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
+                            //await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReplaceItemAsync<Student>(student, student.id, new PartitionKey($"Base-{school}"));
+                            string key = $"Login:School:{school}:student-day:{dateDay}";
+                            //记录一个学校每天每个学生登录的次数
+                            await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync(key, id, 1);
+                            //获取key到期的时间
+                            await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, hour);  //设置到期时间25小时
+                        }
+                        catch (Exception ex) { await _dingDing.SendBotMsg($"{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組); }
+                        break;
+                    case "tmduser":
+                        try
+                        {
+                            //TmdUser tmdUser = await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReadItemAsync<TmdUser>(id, new PartitionKey("Base"));
+                            //tmdUser.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
+                            //await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReplaceItemAsync<TmdUser>(tmdUser, tmdUser.id, new PartitionKey("Base"));
+                        }
+                        catch { }
+                        break;
+                }
+
+                //天
+                SortedSetEntry[] dayCnt = null;
+                //月
+                SortedSetEntry[] monthCnt = null;
+                try
+                {
+                    await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateDay}", $"{currentHour}", 1);//一天24小时  小时为单位
+                    await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateMonth}", $"{currentDay}", 1); //一天的累计   天为单位
+
+                    var resDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateDay}");
+                    if (resDay == null)
+                        await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateDay}", hour);  //设置到期时间
+                    var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateMonth}");
+                    if (rspMonth == null)
+                        await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateMonth}", month);  //设置到期时间
+
+                    //保存当前小时统计
+                    dayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateDay}");
+                    //保存当前的统计数据
+                    monthCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateMonth}");
+                }
+                catch { }
+                if (dayCnt != null && dayCnt.Length > 0)
+                {
+                    List<HourLogin> hourLogins = new();
+                    foreach (var dCnt in dayCnt)
+                    {
+                        if (((int)dCnt.Element) == currentHour)
+                        {
+                            var tphourLogins = await table.QueryWhereString<HourLogin>($"PartitionKey eq 'HourLogin' and RowKey eq '{dateHour}'");
+                            if (tphourLogins.Count > 0)
+                            {
+                                foreach (var hourLogin in tphourLogins)
+                                {
+                                    if (scope.Equals("teacher"))
+                                        hourLogin.Teacher = (int)dCnt.Score;
+                                    else if (scope.Equals("student"))
+                                        hourLogin.Student = (int)dCnt.Score;
+                                    else
+                                        hourLogin.TmdUser = (int)dCnt.Score;
+                                    hourLogins.Add(hourLogin);
+                                }
+                            }
+                            else
+                            {
+                                HourLogin hourLogin = new() { PartitionKey = $"HourLogin", RowKey = dateHour, Hour = currentHour };
+                                if (scope.Equals("teacher"))
+                                {
+                                    hourLogin.Teacher = 1;
+                                    hourLogin.Student = 0;
+                                    hourLogin.TmdUser = 0;
+                                }
+                                else if (scope.Equals("student"))
+                                {
+                                    hourLogin.Teacher = 0;
+                                    hourLogin.Student = 1;
+                                    hourLogin.TmdUser = 0;
+                                }
+                                else
+                                {
+                                    hourLogin.Teacher = 0;
+                                    hourLogin.Student = 0;
+                                    hourLogin.TmdUser = 1;
+                                }
+                                hourLogins.Add(hourLogin);
+                            }
+                        }
+                    }
+                    await table.SaveOrUpdateAll(hourLogins);  //保存和更新保存当前小时登录次数
+                }
+                if (monthCnt != null && monthCnt.Length > 0)
+                {
+                    List<DayLogin> dayLogins = new();
+                    foreach (var mCnt in monthCnt)
+                    {
+                        if (((int)mCnt.Element) == currentDay)
+                        {
+                            //保存当天的峰值
+                            var tbDays = await table.QueryWhereString<DayLogin>($"PartitionKey eq 'DayLogin' and RowKey eq '{dateDay}'");
+                            if (tbDays.Count > 0)
+                            {
+                                foreach (var dayLogin in tbDays)
+                                {
+                                    if (scope.Equals("teacher"))
+                                        dayLogin.Teacher = (int)mCnt.Score;
+                                    else if (scope.Equals("student"))
+                                        dayLogin.Student = (int)mCnt.Score;
+                                    else
+                                        dayLogin.TmdUser = (int)mCnt.Score;
+                                    dayLogins.Add(dayLogin);
+                                }
+                            }
+                            else
+                            {
+                                //保存当月每天的峰值
+                                DayLogin dayLogin = new() { PartitionKey = $"DayLogin", RowKey = dateDay, Day = currentDay };
+                                if (scope.Equals("teacher"))
+                                {
+                                    dayLogin.Teacher = 1;
+                                    dayLogin.Student = 0;
+                                    dayLogin.TmdUser = 0;
+                                }
+                                else if (scope.Equals("student"))
+                                {
+                                    dayLogin.Teacher = 0;
+                                    dayLogin.Student = 1;
+                                    dayLogin.TmdUser = 0;
+                                }
+                                else
+                                {
+                                    dayLogin.Teacher = 0;
+                                    dayLogin.Student = 0;
+                                    dayLogin.TmdUser = 1;
+                                }
+                                dayLogins.Add(dayLogin);
+                            }
+                        }
+                    }
+                    await table.SaveOrUpdateAll(dayLogins);// 保存当月每天在线数据
+                }
+
+                string tbHourSql = $"PartitionKey eq 'HourLogin' and RowKey le '{delTbHour}'";
+
+                string tbDaySql = $"PartitionKey eq 'DayLogin' and RowKey le '{delTbDay}'";
+                try
+                {
+                    await table.DeleteStringWhere<HourLogin>(rowKey: tbHourSql);  //删除168小时前的数据
+                    await table.DeleteStringWhere<DayLogin>(rowKey: tbDaySql);   //删除180天前的数据
+                }
+                catch { }
+
+                if (!string.IsNullOrWhiteSpace(school))
+                {
+                    //天
+                    SortedSetEntry[] scDayCnt = null;
+                    //月
+                    SortedSetEntry[] scMonthCnt = null;
+                    try
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateDay}", $"{currentHour}", 1);//当天当前小时在线人加1
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的在线加1
+
+                        var reScDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateDay}");
+                        if (reScDay == null)
+                            await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateDay}", hour);  //设置到期时间
+                        var reScMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateMonth}");
+                        if (reScMonth == null)
+                            await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateMonth}", month);  //设置到期时间
+
+                        //保存学校当天每小时的
+                        scDayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateDay}");
+                        //学校天峰值
+                        scMonthCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateMonth}");
+                    }
+                    catch { }
+
+                    if (scDayCnt != null && scDayCnt.Length > 0)
+                    {
+                        List<HourLoginSchool> hourLoginSchools = new();
+                        foreach (var scDCnt in scDayCnt)
+                        {
+                            if (((int)scDCnt.Element) == currentHour)
+                            {
+                                var tmpHour = await table.QueryWhereString<HourLoginSchool>($"PartitionKey eq 'HourLogin-{school}' and RowKey eq '{dateHour}'");
+                                if (tmpHour.Count > 0)
+                                {
+                                    foreach (var hLoginSc in tmpHour)
+                                    {
+                                        if (scope.Equals("teacher"))
+                                            hLoginSc.Teacher = (int)scDCnt.Score;
+                                        else if (scope.Equals("student"))
+                                            hLoginSc.Student = (int)scDCnt.Score;
+                                        else
+                                            hLoginSc.TmdUser = (int)scDCnt.Score;
+                                        hourLoginSchools.Add(hLoginSc);
+                                    }
+                                }
+                                else
+                                {
+                                    //学校小时峰值
+                                    HourLoginSchool hourLoginSc = new() { PartitionKey = $"HourLogin-{school}", RowKey = dateHour, Hour = currentHour, School = school };
+                                    if (scope.Equals("teacher"))
+                                    {
+                                        hourLoginSc.Teacher = 1;
+                                        hourLoginSc.Student = 0;
+                                        hourLoginSc.TmdUser = 0;
+                                    }
+                                    else if (scope.Equals("student"))
+                                    {
+                                        hourLoginSc.Teacher = 0;
+                                        hourLoginSc.Student = 1;
+                                        hourLoginSc.TmdUser = 0;
+                                    }
+                                    else
+                                    {
+                                        hourLoginSc.Teacher = 0;
+                                        hourLoginSc.Student = 0;
+                                        hourLoginSc.TmdUser = 1;
+                                    }
+                                    hourLoginSchools.Add(hourLoginSc);
+                                }
+                            }
+                        }
+                        await table.SaveOrUpdateAll(hourLoginSchools);
+                    }
+                    if (scMonthCnt != null && scMonthCnt.Length > 0)
+                    {
+                        List<DayLoginSchool> DayLoginSchools = new();
+                        foreach (var scMCnt in scMonthCnt)
+                        {
+                            if (((int)scMCnt.Element) == currentDay)
+                            {
+                                var tempDays = await table.QueryWhereString<DayLoginSchool>($"PartitionKey eq 'DayLogin-{school}' and RowKey eq '{dateDay}'");
+                                if (tempDays.Count > 0)
+                                {
+                                    foreach (var dLoginSc in tempDays)
+                                    {
+                                        if (scope.Equals("teacher"))
+                                            dLoginSc.Teacher = (int)scMCnt.Score;
+                                        else if (scope.Equals("student"))
+                                            dLoginSc.Student = (int)scMCnt.Score;
+                                        else
+                                            dLoginSc.TmdUser = (int)scMCnt.Score;
+                                        DayLoginSchools.Add(dLoginSc);
+                                    }
+                                }
+                                else
+                                {
+                                    //学校天峰值
+                                    DayLoginSchool dayLoginSc = new() { PartitionKey = $"DayLogin-{school}", RowKey = dateDay, Day = currentDay, School = school };
+                                    if (scope.Equals("teacher"))
+                                    {
+                                        dayLoginSc.Teacher = 1;
+                                        dayLoginSc.Student = 0;
+                                        dayLoginSc.TmdUser = 0;
+                                    }
+                                    else if (scope.Equals("student"))
+                                    {
+                                        dayLoginSc.Teacher = 0;
+                                        dayLoginSc.Student = 1;
+                                        dayLoginSc.TmdUser = 0;
+                                    }
+                                    else
+                                    {
+                                        dayLoginSc.Teacher = 0;
+                                        dayLoginSc.Student = 0;
+                                        dayLoginSc.TmdUser = 1;
+                                    }
+
+                                    DayLoginSchools.Add(dayLoginSc);
+                                }
+                            }
+                        }
+                        await table.SaveOrUpdateAll(DayLoginSchools);//保存学校当月在线数据
+                    }
+
+                    string tbScHourSql = $"PartitionKey eq 'HourLogin-{school}' and RowKey le '{delTbHour}'";
+                    List<HourLogin> scHourLog = await table.QueryWhereString<HourLogin>(tbScHourSql);
+                    if (scHourLog.Count > 0)
+                        try
+                        {
+                            //await table.DeleteStringWhere<HourLogin>(tbScHourSql); //删除学校168小时前的数据
+                            await table.DeleteAll(scHourLog);
+                        }
+                        catch { }
+                    string tbScDaySql = $"PartitionKey eq 'DayLogin-{school}' and RowKey le '{delTbDay}'";
+                    List<DayLogin> scDayLog = await table.QueryWhereString<DayLogin>(tbScDaySql);
+                    if (scDayLog.Count > 0)
+                        try
+                        {
+                            //await table.DeleteStringWhere<DayLogin>(tbScDaySql); //删除学校180天前的数据
+                            await table.DeleteAll(scDayLog);
+                        }
+                        catch { }
+                }
+
+                return new OkObjectResult(new {  data=json});
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-online-record 人数记录异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return new OkObjectResult(new { data = json });
+            }
+        }
+    }
+}

File diff suppressed because it is too large
+ 2063 - 0
TEAMModelOS.Function/IESServiceBusTrigger.cs


+ 105 - 0
TEAMModelOS.Function/IESTimerTrigger.cs

@@ -0,0 +1,105 @@
+using System;
+using DinkToPdf.Contracts;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.Extension;
+
+namespace TEAMModelOS.Function
+{
+    public class IESTimerTrigger
+    {
+        private readonly ILogger _logger;
+        /// <summary>
+        /// 文档。https://docs.microsoft.com/zh-cn/azure/azure-functions/functions-bindings-timer?tabs=in-process&pivots=programming-language-csharp
+        /// Timer 在线测试  https://ncrontab.swimburger.net/
+        /// </summary>
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+        //private readonly IConverter _converter;
+        private readonly SnowflakeId _snowflakeId;
+        private readonly IHttpClientFactory _httpClient;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        //private IPSearcher _ipSearcher;
+        private readonly IConfiguration _configuration;
+        public IESTimerTrigger(ILoggerFactory loggerFactory, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, IHttpClientFactory httpClient, SnowflakeId snowflakeId,   AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis)
+        {
+            _logger = loggerFactory.CreateLogger<IESTimerTrigger>();
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
+          //  _converter = converter;
+            _snowflakeId=snowflakeId;
+            _httpClient = httpClient;
+            //  _ipSearcher = ipSearcher;
+            _coreAPIHttpService = coreAPIHttpService;
+            _configuration= configuration;
+        }
+
+
+
+        /// <summary>
+        /// //0 1 * * * * 一天中每小时的第 1 分钟
+        ///0 */10 * * * *  每五分钟一次
+        /// </summary>
+        /// <param name="myTimer"></param>
+        /// <returns></returns>
+        [Function("DailyReport")]
+        public async Task DailyReport([TimerTrigger("0 1 * * * *")] TimerInfo myTimer)
+        {
+            _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
+            try
+            {
+                await _dingDing.SendBotMsg($"{_coreAPIHttpService.options.location},定时任务{DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss")}", GroupNames.成都开发測試群組);
+                string local = Environment.GetEnvironmentVariable("Option:Location");
+                // if (local.Contains("Test")||local.Contains("Dep"))
+                {
+
+                    int am = 9;
+                    int pm = 9;
+                    //int am = DateTimeOffset.Now.Hour;
+                    //int pm = DateTimeOffset.Now.Hour;
+                    var url = Environment.GetEnvironmentVariable("HaBookAuth:CoreAPI");
+                    var clientID = Environment.GetEnvironmentVariable("HaBookAuth:CoreService:clientID");
+                    var clientSecret = Environment.GetEnvironmentVariable("HaBookAuth:CoreService:clientSecret");
+                    string location = "";
+                    if (local.Contains("China"))
+                    {
+                        location = "China";
+                    }
+                    else if (local.Contains("Global"))
+                    {
+                        location = "Global";
+                    }
+                    var client = _httpClient.CreateClient();
+                    var token = CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location).Result;
+                    if (client.DefaultRequestHeaders.Contains("Authorization"))
+                    {
+                        client.DefaultRequestHeaders.Remove("Authorization");
+                        client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+                    }
+                    else
+                    {
+                        client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+                    }
+                    var data = await TEAMModelOS.SDK.Models.Service.SystemService.AccumulateDaily(_configuration, _azureRedis, _azureCosmos, _coreAPIHttpService, _dingDing, client, _snowflakeId, url, am, pm);
+                    //await _dingDing.SendBotMsg($"返回数据{data.ToJsonString()}", GroupNames.成都开发測試群組);
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{DailyReport}\n{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+            }
+
+            if (myTimer.ScheduleStatus is not null)
+            {
+                _logger.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus.Next}");
+            }
+        }
+    }
+}

+ 31 - 0
TEAMModelOS.Function/Lang/en-us.json

@@ -0,0 +1,31 @@
+{
+  "request_school": [ "Apply to join school", "{tmdname}({tmdid}) is applying to join {schoolName}.", "Agree", "Refuse" ],
+  "invite_school": [ "Invite to join school", "{schoolName} has invited you to join.", "Accept", "Decline" ],
+  "remove_school": [ "Remove from school", "{schoolName} removed you from the school's teacher list." ],
+  "request-join_school": [ "Agree to join school", "{schoolName} has agreed you to join the school." ],
+  "invite-join_school": [ "Accepting join school invitation", "{tmdname} has accepted an invitation to join {schoolName}." ],
+  "coedit_syllabus": [ "Invite to co-edit syllabus", "{tmdname} of {schoolName} has invited you to co-edit school-based syllabus, Volume name: {volumeName}, Node name: {syllabusName}." ],
+  "coedit_volume": [ "Invite to co-edit volume", "{tmdname} of {schoolName} has invited  you to co-edit school-based volume,Volume name:{volumeName}." ],
+  "share_syllabus": [ "Receive shared syllabus", "{tmdname} shared personal syllabus with you, Volume name: {volumeName}, Node name: {syllabusName}." ],
+  "transfer-admin_school": [ "Transfer administrator", "{tmdname} will transfer the administrator of {schoolName} to you." ],
+  "scoring-arb_school": [ "Assign arbitration exam scoring task", "{tmdname} of {schoolName} has assign you an arbitration exam scoring task." ],
+  "scoring-err_school": [ "Assign abnormal exam paper grading task", "{tmdname} of {schoolName} has assign you an abnormal exam paper grading task." ],
+  "scoring-mark_school": [ "Assign exam paper grading task", "{tmdname} of {schoolName} has assign you an exam paper grading task." ],
+  "scan-join_groupList": [ "Join course notice", "{tmdname} join the {groupListName} course via QRcode scanning" ],
+  "scan-join_school": [ "Join school notice", "{tmdname} join school, {schoolName}, via QRcode scanning" ],
+  "submitanswer-school_homework": [ "Homework submission notice", "{tmdname} of {schoolName} has submitted a homework:{homeworkName}" ],
+  "submitanswer-private_homework": [ "Homework submission notice", "{tmdname} has submitted a homework({homeworkName})" ],
+  "expire-school_lessonRecord": [ "Lesson record expiration notice", "Your lesson record, {lessonName}, on {schoolName} will expire at {expireTime}" ],
+  "expire-private_lessonRecord": [ "Lesson record expiration notice", "Your lesson record, {lessonName} will expire at {expireTime}" ],
+  "create-school": [ "Create schools in batches", "{tmdname}You successfully created schools in batch with Bi" ],
+  "copy-file_area": [ "Batch copy file start", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次考核中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次考核中{quotasLow}等指标有待提高,希望进一步加强。",
+  "art-template-comment4": "你在本次考核中{pointHigh}等知识点取得很好的成绩,希望继续保持。",
+  "art-template-comment5": "你在本次考核中{pointLow}等知识有待提高,希望进一步加强。",
+  "art-template-subject_music": "其实音乐殿堂的门槛并没有你想的那么高,对吧?希望你继续努力能够在这里欣赏到更美丽的风景!跳动的音符充满魅力,每个人的生活都离不开音乐,加油吧,相信你在音乐中会有所收获的。",
+  "art-template-subject_painting": "你敢于探素,乐于欣赏,在五彩的画笔中快乐成长,如果你再多一点耐心、仔细刻画,一定会更棒!你积极热爱美术,总是充满快乐,希望你能够坚持美术学习,永不放弃,在美术的道路上快乐地成长!",
+  "blob-space-school-notify": [ "There is not enough space left", "Dear {schoolName} administrator {tmdname}, remind you that the remaining space of your school is less than {percent}%, please contact the sales to purchase space, for details, please log in to IES (teammodel.net ) to view. Please ignore this notification if the capacity has already been expanded." ],
+  "blob-space-private-notify": [ "There is not enough space left", "Hello, {tmdname}, remind you that the remaining space belong to your account {tmdid} is less than {percent}%. Please contact the school administrator to configure or purchase your own space. Please ignore this notification if the capacity has already been expanded." ]
+}

+ 31 - 0
TEAMModelOS.Function/Lang/zh-cn.json

@@ -0,0 +1,31 @@
+{
+  "request_school": [ "申请加入学校通知", "{tmdname}({tmdid})申请加入{schoolName}。", "同意", "拒绝" ],
+  "invite_school": [ "邀请加入学校通知", "{schoolName}邀请您加入学校。", "接受", "婉拒" ],
+  "remove_school": [ "从学校移除通知", "{schoolName}将您从学校教师名单中移除。" ],
+  "request-join_school": [ "同意申请加入学校通知", "{schoolName}已同意您申请加入学校。" ],
+  "invite-join_school": [ "同意邀请加入学校通知", "{tmdname}已接受加入{schoolName}的邀请。" ],
+  "coedit_syllabus": [ "邀请共编课纲通知", "{schoolName}的{tmdname}邀请你参与共编校本课纲,册别名称:{volumeName},课纲节点名称:{syllabusName}。" ],
+  "coedit_volume": [ "邀请共编册别通知", "{schoolName}的{tmdname}邀请你参与共编校本册别,册别名称:{volumeName}。" ],
+  "share_syllabus": [ "课纲分享接收通知", "{tmdname}向您分享了个人课纲,册别名称:{volumeName},课纲节点名称:{syllabusName}。" ],
+  "transfer-admin_school": [ "管理员移交通知", "{tmdname}将{schoolName}的管理员移交给您。" ],
+  "scoring-arb_school": [ "仲裁卷阅卷任务通知", "{schoolName}的{tmdname}向您发送了仲裁卷阅卷任务。" ],
+  "scoring-err_school": [ "异常卷阅卷任务通知", "{schoolName}的{tmdname}向您发送了异常卷阅卷任务。" ],
+  "scoring-mark_school": [ "普通阅卷任务通知", "{schoolName}的{tmdname}向您发送了普通卷阅卷任务。" ],
+  "scan-join_groupList": [ "扫码加入名单通知", "{tmdname}扫码加入名单,名单:{groupListName}" ],
+  "scan-join_school": [ "扫码加入学校通知", "{tmdname}扫码加入学校,学校名称:{schoolName}" ],
+  "submitanswer-school_homework": [ "作业提交通知", "{schoolName}的{tmdname}已提交作业,作业名称:{homeworkName}" ],
+  "submitanswer-private_homework": [ "作业提交通知", "{tmdname}已提交作业,作业名称({homeworkName})" ],
+  "expire-school_lessonRecord": [ "课例到期通知", "您在{schoolName}的课例将在{expireTime}到期,课例名称:{lessonName}" ],
+  "expire-private_lessonRecord": [ "课例到期通知", "您的课例将在{expireTime}到期,课例名称:{lessonName}" ],
+  "create-school": [ "批量创建学校", "{tmdname}您用BI批量创建学校成功" ],
+  "copy-file_area": [ "批复制文件开始", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次考核中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次考核中{quotasLow}等指标有待提高,希望进一步加强。",
+  "art-template-comment4": "你在本次考核中{pointHigh}等知识点取得很好的成绩,希望继续保持。",
+  "art-template-comment5": "你在本次考核中{pointLow}等知识有待提高,希望进一步加强。",
+  "art-template-subject_music": "其实音乐殿堂的门槛并没有你想的那么高,对吧?希望你继续努力能够在这里欣赏到更美丽的风景!跳动的音符充满魅力,每个人的生活都离不开音乐,加油吧,相信你在音乐中会有所收获的。",
+  "art-template-subject_painting": "你敢于探素,乐于欣赏,在五彩的画笔中快乐成长,如果你再多一点耐心、仔细刻画,一定会更棒!你积极热爱美术,总是充满快乐,希望你能够坚持美术学习,永不放弃,在美术的道路上快乐地成长!",
+  "blob-space-school-notify": [ "剩余空间不足", "亲爱的{schoolName}管理员{tmdname}您好,提醒您贵校剩余空间不足{percent}%,请联系官方客服购买空间,详情请登入IES(teammodel.net)查看,如果已经扩容请忽略此通知。" ],
+  "blob-space-private-notify": [ "剩余空间不足", "亲爱的{tmdname}您好,提醒您的帐号{tmdid}授权的剩余空间不足{percent}%,请联系学校管理员配置或是购买个人空间,如果已经扩容请忽略此通知。" ]
+}

+ 31 - 0
TEAMModelOS.Function/Lang/zh-tw.json

@@ -0,0 +1,31 @@
+{
+  "request_school": [ "申請加入學校通知", "{tmdname}({tmdid})申請加入{schoolName}。", "同意", "拒絕" ],
+  "invite_school": [ "邀請加入學校通知", "{schoolName}邀請您加入學校。", "接受", "婉拒" ],
+  "remove_school": [ "從學校移除通知", "{schoolName}將您從學校教師名單中移除。" ],
+  "request-join_school": [ "同意申請加入學校通知", "{schoolName}已同意您申請加入學校。" ],
+  "invite-join_school": [ "同意邀請加入學校通知", "{tmdname}已接受加入{schoolName}的邀請。" ],
+  "coedit_syllabus": [ "邀請共編課綱通知", "{schoolName}的{tmdname}邀請你參與共編校本課綱,冊別名稱:{volumeName},課綱節點名稱:{syllabusName}。" ],
+  "coedit_volume": [ "邀請共編冊別通知", "{schoolName}的{tmdname}邀請你參與共編校本冊別,冊別名稱:{volumeName}。" ],
+  "share_syllabus": [ "課綱分享接收通知", "{tmdname}向您分享了個人課綱,冊別名稱:{volumeName},課綱節點名稱:{syllabusName}。" ],
+  "transfer-admin_school": [ "管理員移交通知", "{tmdname}將{schoolName}的管理員移交給您。" ],
+  "scoring-arb_school": [ "仲裁卷閱卷任務通知", "{schoolName}的{tmdname}向您發送了仲裁卷閱卷任務。" ],
+  "scoring-err_school": [ "異常卷閱卷任務通知", "{schoolName}的{tmdname}向您發送了異常卷閱卷任務。" ],
+  "scoring-mark_school": [ "普通閱卷任務通知", "{schoolName}的{tmdname}向您發送了普通卷閱卷任務。" ],
+  "scan-join_groupList": [ "掃碼加入名單通知", "{tmdname}掃碼加入名單,名單:{groupListName}" ],
+  "scan-join_school": [ "掃碼加入學校通知", "{tmdname}掃碼加入學校,學校名稱:{schoolName}" ],
+  "submitanswer-school_homework": [ "作業提交通知", "{schoolName}的{tmdname}已提交作業,作業名稱:{homeworkName}" ],
+  "submitanswer-private_homework": [ "作業提交通知", "{tmdname}已提交作業,作業名稱({homeworkName})" ],
+  "expire-school_lessonRecord": [ "課例到期通知", "您在{schoolName}的課例將在{expireTime}到期,課例名稱:{lessonName}" ],
+  "expire-private_lessonRecord": [ "課例到期通知", "您的課例將在{expireTime}到期,課例名稱:{lessonName}" ],
+  "create-school": [ "批量創建學校", "{tmdname}您用BI批量創建學校成功" ],
+  "copy-file_area": [ "批復制文件開始", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次考核中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次考核中{quotasLow}等指标有待提高,希望进一步加强。",
+  "art-template-comment4": "你在本次考核中{pointHigh}等知识点取得很好的成绩,希望继续保持。",
+  "art-template-comment5": "你在本次考核中{pointLow}等知识有待提高,希望进一步加强。",
+  "art-template-subject_music": "其实音乐殿堂的门槛并没有你想的那么高,对吧?希望你继续努力能够在这里欣赏到更美丽的风景!跳动的音符充满魅力,每个人的生活都离不开音乐,加油吧,相信你在音乐中会有所收获的。",
+  "art-template-subject_painting": "你敢于探素,乐于欣赏,在五彩的画笔中快乐成长,如果你再多一点耐心、仔细刻画,一定会更棒!你积极热爱美术,总是充满快乐,希望你能够坚持美术学习,永不放弃,在美术的道路上快乐地成长!",
+  "blob-space-school-notify": [ "剩餘空間不足", "親愛的{schoolName}管理員{tmdname}您好,提醒您貴校剩餘空間不足{percent}%,請聯繫官方客服購買空間,詳情請登入IES(teammodel.net)查看,如果已經擴容請忽略此通知。" ],
+  "blob-space-private-notify": [ "剩餘空間不足", "親愛的{tmdname}您好,提醒您的帳號{tmdid}授權的剩餘空間不足{percent}%,請聯繫學校管理員配置或是購買個人空間,如果已經擴容請忽略此通知。" ]
+}

+ 43 - 0
TEAMModelOS.Function/Program.cs

@@ -0,0 +1,43 @@
+
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using TEAMModelOS.Function.DI;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.DI.Multiple;
+var host = new HostBuilder()
+    .ConfigureFunctionsWebApplication()
+    .ConfigureServices((context, services) =>
+    {
+        services.AddApplicationInsightsTelemetryWorkerService();
+        services.ConfigureFunctionsApplicationInsights();
+
+
+
+        services.AddHttpClient();
+        services.AddHttpClient<DingDing>();
+        services.AddHttpClient<HttpTrigger>();
+        services.AddHttpClient<CoreAPIHttpService>();
+        List<(string name, string connectionString)> cosmosDBConnects = new();
+        cosmosDBConnects.Add(("Default", context.Configuration.GetSection("Azure:Cosmos:ConnectionString").Get<string>()));
+        cosmosDBConnects.Add(("CoreServiceV1", context.Configuration.GetSection("CoreServiceV1:Cosmos:ConnectionString").Get<string>())); //CoreService V1 read only
+        cosmosDBConnects.Add(("CoreServiceV2", context.Configuration.GetSection("CoreServiceV2:Cosmos:ConnectionString").Get<string>())); //CoreService V2
+        cosmosDBConnects.Add(("CoreServiceV2CnRead", context.Configuration.GetSection("CoreServiceV2:CosmosCnRead:ConnectionString").Get<string>())); //CoreService V2 CN read only
+        services.AddMultipleAzureCosmos(cosmosDBConnects);
+
+        services.AddAzureServiceBus(context.Configuration.GetSection("Azure:ServiceBus:ConnectionString").Get<string>());
+
+        services.AddAzureRedis(context.Configuration.GetSection("Azure:Redis:ConnectionString").Get<string>());
+        services.AddSnowflakeId(Convert.ToInt64(context.Configuration.GetSection("Option:LocationNum").Get<Int32>()), 1);
+        List<(string name, string connectionString)> storageConnects = new();
+        storageConnects.Add(("Default", context.Configuration.GetSection("Azure:Storage:ConnectionString").Get<string>()));
+        services.AddMultipleAzureStorage(storageConnects);
+
+        services.AddSingleton<BackgroundWorkerQueue>();
+        services.AddHostedService<LongRunningService>();
+    })
+    .Build();
+
+host.Run();

+ 174 - 0
TEAMModelOS.Function/Properties/ServiceDependencies/teammodelosfunction-test - Zip Deploy/profile.arm.json

@@ -0,0 +1,174 @@
+{
+  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
+  "contentVersion": "1.0.0.0",
+  "metadata": {
+    "_dependencyType": "compute.function.windows.appService"
+  },
+  "parameters": {
+    "resourceGroupName": {
+      "type": "string",
+      "defaultValue": "TEAMModelChengdu",
+      "metadata": {
+        "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
+      }
+    },
+    "resourceGroupLocation": {
+      "type": "string",
+      "defaultValue": "",
+      "metadata": {
+        "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
+      }
+    },
+    "resourceName": {
+      "type": "string",
+      "defaultValue": "test",
+      "metadata": {
+        "description": "Name of the main resource to be created by this template."
+      }
+    },
+    "resourceLocation": {
+      "type": "string",
+      "defaultValue": "[parameters('resourceGroupLocation')]",
+      "metadata": {
+        "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
+      }
+    }
+  },
+  "resources": [
+    {
+      "type": "Microsoft.Resources/resourceGroups",
+      "name": "[parameters('resourceGroupName')]",
+      "location": "[parameters('resourceGroupLocation')]",
+      "apiVersion": "2019-10-01"
+    },
+    {
+      "type": "Microsoft.Resources/deployments",
+      "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+      "resourceGroup": "[parameters('resourceGroupName')]",
+      "apiVersion": "2019-10-01",
+      "dependsOn": [
+        "[parameters('resourceGroupName')]"
+      ],
+      "properties": {
+        "mode": "Incremental",
+        "expressionEvaluationOptions": {
+          "scope": "inner"
+        },
+        "parameters": {
+          "resourceGroupName": {
+            "value": "[parameters('resourceGroupName')]"
+          },
+          "resourceGroupLocation": {
+            "value": "[parameters('resourceGroupLocation')]"
+          },
+          "resourceName": {
+            "value": "[parameters('resourceName')]"
+          },
+          "resourceLocation": {
+            "value": "[parameters('resourceLocation')]"
+          }
+        },
+        "template": {
+          "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+          "contentVersion": "1.0.0.0",
+          "parameters": {
+            "resourceGroupName": {
+              "type": "string"
+            },
+            "resourceGroupLocation": {
+              "type": "string"
+            },
+            "resourceName": {
+              "type": "string"
+            },
+            "resourceLocation": {
+              "type": "string"
+            }
+          },
+          "variables": {
+            "storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]",
+            "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+            "storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]",
+            "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]",
+            "function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]"
+          },
+          "resources": [
+            {
+              "location": "[parameters('resourceLocation')]",
+              "name": "[parameters('resourceName')]",
+              "type": "Microsoft.Web/sites",
+              "apiVersion": "2015-08-01",
+              "tags": {
+                "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
+              },
+              "dependsOn": [
+                "[variables('appServicePlan_ResourceId')]",
+                "[variables('storage_ResourceId')]"
+              ],
+              "kind": "functionapp",
+              "properties": {
+                "name": "[parameters('resourceName')]",
+                "kind": "functionapp",
+                "httpsOnly": true,
+                "reserved": false,
+                "serverFarmId": "[variables('appServicePlan_ResourceId')]",
+                "siteConfig": {
+                  "alwaysOn": true
+                }
+              },
+              "identity": {
+                "type": "SystemAssigned"
+              },
+              "resources": [
+                {
+                  "name": "appsettings",
+                  "type": "config",
+                  "apiVersion": "2015-08-01",
+                  "dependsOn": [
+                    "[variables('function_ResourceId')]"
+                  ],
+                  "properties": {
+                    "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]",
+                    "FUNCTIONS_EXTENSION_VERSION": "~3",
+                    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
+                  }
+                }
+              ]
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('storage_name')]",
+              "type": "Microsoft.Storage/storageAccounts",
+              "apiVersion": "2017-10-01",
+              "tags": {
+                "[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty"
+              },
+              "properties": {
+                "supportsHttpsTrafficOnly": true
+              },
+              "sku": {
+                "name": "Standard_LRS"
+              },
+              "kind": "Storage"
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('appServicePlan_name')]",
+              "type": "Microsoft.Web/serverFarms",
+              "apiVersion": "2015-08-01",
+              "sku": {
+                "name": "S1",
+                "tier": "Standard",
+                "family": "S",
+                "size": "S1"
+              },
+              "properties": {
+                "name": "[variables('appServicePlan_name')]"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ]
+}

+ 9 - 0
TEAMModelOS.Function/Properties/launchSettings.json

@@ -0,0 +1,9 @@
+{
+  "profiles": {
+    "TEAMModelOS.Function": {
+      "commandName": "Project",
+      "commandLineArgs": "--port 7201",
+      "launchBrowser": false
+    }
+  }
+}

+ 12 - 0
TEAMModelOS.Function/Properties/serviceDependencies.json

@@ -0,0 +1,12 @@
+{
+  "dependencies": {
+    "storage1": {
+      "type": "storage",
+      "connectionId": "AzureWebJobsStorage"
+    },
+    "appInsights1": {
+      "type": "appInsights",
+      "connectionId": "APPLICATIONINSIGHTS_CONNECTION_STRING"
+    }
+  }
+}

+ 11 - 0
TEAMModelOS.Function/Properties/serviceDependencies.local.json

@@ -0,0 +1,11 @@
+{
+  "dependencies": {
+    "appInsights1": {
+      "type": "appInsights.sdk"
+    },
+    "storage1": {
+      "type": "storage.emulator",
+      "connectionId": "AzureWebJobsStorage"
+    }
+  }
+}

+ 14 - 0
TEAMModelOS.Function/Properties/serviceDependencies.teammodelosfunction-test - Zip Deploy.json

@@ -0,0 +1,14 @@
+{
+  "dependencies": {
+    "storage1": {
+      "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.Storage/storageAccounts/teammodellog",
+      "type": "storage.azure",
+      "connectionId": "AzureWebJobsStorage"
+    },
+    "appInsights1": {
+      "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/microsoft.insights/components/teammodelosfunctiontest",
+      "type": "appInsights.azure",
+      "connectionId": "APPLICATIONINSIGHTS_CONNECTION_STRING"
+    }
+  }
+}

+ 58 - 0
TEAMModelOS.Function/TEAMModelOS.Function.csproj

@@ -0,0 +1,58 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
+    <OutputType>Exe</OutputType>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+  <ItemGroup>
+    <None Remove="Lang\en-us.json" />
+    <None Remove="Lang\zh-cn.json" />
+    <None Remove="Lang\zh-tw.json" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Lang\en-us.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+    <Content Include="Lang\zh-cn.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+    <Content Include="Lang\zh-tw.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <FrameworkReference Include="Microsoft.AspNetCore.App" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.22.0" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.0.0-preview2" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.20.0" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.1" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.1" />
+    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
+    <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Update="host.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Update="local.settings.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
+  </ItemGroup>
+</Project>

+ 12 - 0
TEAMModelOS.Function/host.json

@@ -0,0 +1,12 @@
+{
+    "version": "2.0",
+    "logging": {
+        "applicationInsights": {
+            "samplingSettings": {
+                "isEnabled": true,
+                "excludedTypes": "Request"
+            },
+            "enableLiveMetricsFilters": true
+        }
+    }
+}

+ 11 - 0
TEAMModelOS.Function/readme.md

@@ -0,0 +1,11 @@
+# TimerTrigger - C<span>#</span>
+
+The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function every 5 minutes.
+
+## How it works
+
+For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year".
+
+## Learn more
+
+<TODO> Documentation

+ 0 - 9
TEAMModelOS.FunctionV4/Properties/serviceDependencies.TEAMModelFunction - Zip Deploy.json

@@ -1,9 +0,0 @@
-{
-  "dependencies": {
-    "storage1": {
-      "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.Storage/storageAccounts/teammodellog",
-      "type": "storage.azure",
-      "connectionId": "AzureWebJobsStorage"
-    }
-  }
-}

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

@@ -44,7 +44,6 @@ using Microsoft.Azure.Amqp.Framing;
 using System.Security.Policy;
 using TEAMModelOS.SDK.Models.Service.BI;
 using TEAMModelOS.SDK.Models.Dtos;
-using System.ServiceModel.Channels;
 using static TEAMModelOS.FunctionV4.Program;
 
 namespace TEAMModelOS.FunctionV4.ServiceBus

+ 1 - 2
TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj

@@ -57,8 +57,7 @@
 		<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.15.0" />
 		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.1.0" />
 		<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" />
-	
-		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
+		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
 		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.1" />
 		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.7.0" />
 		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.0.0-preview2" />

+ 2 - 0
TEAMModelOS.FunctionV4/local.settings.json

@@ -1,6 +1,8 @@
 {
   "IsEncrypted": false,
   "Values": {
+    "WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED": "1",
+    "FUNCTIONS_EXTENSION_VERSION": "~4",
     "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=teammodellog;AccountKey=lxVDrgs+6rKtmASL3k1WrarrEd5Rk42wS1Mu5+sqQlPya1JLSlFDtnZUvMPeHHe7zlESfn/1NY7CZdGviy2UCw==;EndpointSuffix=core.chinacloudapi.cn",
     "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
     "Azure:Storage:ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodeltest;AccountKey=O2W2vadCqexDxWO+px+QK7y1sHwsYj8f/WwKLdOdG5RwHgW/Dupz9dDUb4c1gi6ojzQaRpFUeAAmOu4N9E+37A==;EndpointSuffix=core.chinacloudapi.cn",

+ 0 - 2
TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosExtensions.cs

@@ -1,5 +1,3 @@
-using Microsoft.Azure.Cosmos.Table;
-using Microsoft.Azure.Cosmos.Table.Queryable;
 using System;
 using System.Collections;
 using System.Collections.Generic;

+ 9 - 7
TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactory.cs

@@ -1,8 +1,11 @@
 using Azure.Cosmos;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using System;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace TEAMModelOS.SDK.DI
 {
@@ -16,13 +19,12 @@ namespace TEAMModelOS.SDK.DI
 
         //   private CosmosDatabase database { get; set; }
 
-        public AzureCosmosFactory(IServiceProvider services, IOptionsMonitor<AzureCosmosFactoryOptions> optionsMonitor )
+        public AzureCosmosFactory(IServiceProvider services, IOptionsMonitor<AzureCosmosFactoryOptions> optionsMonitor)
         {
             if (services == null) throw new ArgumentNullException(nameof(services));
             if (optionsMonitor == null) throw new ArgumentNullException(nameof(optionsMonitor));
-
+           _optionsMonitor = optionsMonitor;
             _services = services;
-            _optionsMonitor = optionsMonitor;            
         }
 
         /// <summary>
@@ -35,14 +37,14 @@ namespace TEAMModelOS.SDK.DI
         {
             try
             {
-                //CosmosClientOptions 的 SerializerOptions = new CosmosSerializationOptions() { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase } 
+                //CosmosClientOptions 的 SerializerOptions = new CosmosSerializationOptions() { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase }
                 //需等待官方修正
-                var cm = CosmosClients.GetOrAdd(name, x => new CosmosClient(_optionsMonitor.Get(name).CosmosConnectionString, new CosmosClientOptions() {  ApplicationRegion = region }));
-                return cm;
+                var cm = CosmosClients.GetOrAdd(name, x => new CosmosClient(_optionsMonitor.Get(name).CosmosConnectionString, new CosmosClientOptions() { ApplicationRegion = region }));
+                return cm; ;
             }
             catch (Exception e)
             {
-                return null;
+               throw new Exception($"GetCosmosClient error:{e.Message}{e.StackTrace}");
             }
         }        
 

+ 0 - 2
TEAMModelOS.SDK/DI/AzureServiceBus/AzureServiceBusExtensions.cs

@@ -1,5 +1,3 @@
-using Microsoft.Azure.Cosmos.Table;
-using Microsoft.Azure.Cosmos.Table.Queryable;
 using System;
 using System.Collections;
 using System.Collections.Generic;

+ 0 - 8
TEAMModelOS.SDK/DI/AzureServiceBus/AzureServiceBusFactory.cs

@@ -1,15 +1,7 @@
 
-using Microsoft.Azure.Cosmos.Table;
 using Microsoft.Extensions.Options;
 using Microsoft.Extensions.Logging;
 using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Extensions.DependencyInjection;
-using Azure.Storage.Blobs;
-using Azure.Storage.Blobs.Models;
-using Azure.Storage.Blobs.Specialized;
-using StackExchange.Redis;
 using System.Collections.Concurrent;
 using Azure.Messaging.ServiceBus;
 

+ 0 - 1
TEAMModelOS.SDK/DI/HttpTrigger/WebHookHttpTrigger.cs

@@ -1,6 +1,5 @@
 using Azure.Cosmos;
 using Azure.Storage.Blobs.Models;
-using Grpc.Core;
 using HTEXLib.COMM.Helpers;
 using Microsoft.AspNetCore;
 using Microsoft.AspNetCore.Http;

+ 9 - 3
TEAMModelOS.SDK/DI/Multiple/MultipleAzureCosmosFactoryExtensions.cs

@@ -1,5 +1,6 @@
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
 using System;
 using System.Collections.Generic;
 using System.Text;
@@ -20,12 +21,17 @@ namespace TEAMModelOS.SDK.DI.Multiple
         {
             if (services == null) throw new ArgumentNullException(nameof(services));
             if (connectionInfo == null) throw new ArgumentNullException(nameof(connectionInfo));
-            services.TryAddSingleton<AzureCosmosFactory>();
+          
             connectionInfo.ForEach(connection =>
             {
                 services.Configure<AzureCosmosFactoryOptions>(connection.name, o => { o.Name = connection.name; o.CosmosConnectionString = connection.connectionString; });
-            });  //多个数据库注入
-            //services.Configure<AzureCosmosFactoryOptions>(name, o => { o.Name = name; o.CosmosConnectionString = connectionString; });  //单个数据库注入
+               
+            });
+            services.AddSingleton<IOptionsMonitor<AzureCosmosFactoryOptions>, OptionsMonitor<AzureCosmosFactoryOptions>>();
+            services.TryAddSingleton<AzureCosmosFactory>();
+           
+            //多个数据库注入
+           
             return services;
         }
     }

+ 0 - 46
TEAMModelOS.SDK/Helper/Common/JsonHelper/JsonPatchHelper.cs

@@ -1,46 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.AspNetCore.JsonPatch;
-using Microsoft.AspNetCore.JsonPatch.Operations;
-namespace System
-{
-    public static class JsonPatchHelper
-    {
-        public static T JsonAdd<T>(this T t, string path, object value) where T : class
-        {
-            JsonPatchDocument<T> jsonPatch = new JsonPatchDocument<T>();
-            jsonPatch.Operations.Add(new Operation<T>("add", path, null, value));
-            jsonPatch.ApplyTo(t);
-            return t;
-        }
-        public static T JsonRemove<T>(this T t, string path) where T : class
-        {
-            JsonPatchDocument<T> jsonPatch = new JsonPatchDocument<T>();
-            jsonPatch.Operations.Add(new Operation<T>("remove", path, null, null));
-            jsonPatch.ApplyTo(t);
-            return t;
-        }
-        public static T JsonReplace<T>(this T t, string path, object value) where T : class
-        {
-            JsonPatchDocument<T> jsonPatch = new JsonPatchDocument<T>();
-            jsonPatch.Operations.Add(new Operation<T>("replace", path, null, value));
-            jsonPatch.ApplyTo(t);
-            return t;
-        }
-        public static T JsonMove<T>(this T t, string from, string path) where T : class
-        {
-            JsonPatchDocument<T> jsonPatch = new JsonPatchDocument<T>();
-            jsonPatch.Operations.Add(new Operation<T>("move", path, from));
-            jsonPatch.ApplyTo(t);
-            return t;
-        }
-        public static T JsonCopy<T>(this T t, string from, string path) where T : class
-        {
-            JsonPatchDocument<T> jsonPatch = new JsonPatchDocument<T>();
-            jsonPatch.Operations.Add(new Operation<T>("copy", path, from));
-            jsonPatch.ApplyTo(t);
-            return t;
-        }
-    }
-}

+ 0 - 3
TEAMModelOS.SDK/Helper/Common/StringHelper/PingYinHelper.cs

@@ -1,8 +1,5 @@
 using Microsoft.International.Converters.PinYinConverter;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using NUnit.Framework.Internal;
 using System;
-using System.Collections.Generic;
 using System.Text;
 
 namespace TEAMModelOS.SDK

+ 1 - 2
TEAMModelOS.SDK/Models/Cosmos/Common/StudentScoreRecord.cs

@@ -1,5 +1,4 @@
-using NUnit.Framework.Internal.Execution;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;

+ 1 - 5
TEAMModelOS.SDK/Models/Service/BI/BICommonWay.cs

@@ -1,11 +1,7 @@
-using DocumentFormat.OpenXml.Bibliography;
-using MathNet.Numerics.LinearAlgebra.Double;
-using NUnit.Framework;
+using MathNet.Numerics.LinearAlgebra.Double;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Text;
-using System.Threading.Tasks;
 
 namespace TEAMModelOS.SDK.Models.Service.BI
 {

+ 0 - 2
TEAMModelOS.SDK/Models/Service/BI/BIStats.cs

@@ -1,9 +1,7 @@
 using Azure.Cosmos;
 using Azure.Storage.Blobs;
-using DocumentFormat.OpenXml.Math;
 using MathNet.Numerics.LinearAlgebra.Double;
 using Microsoft.Azure.Cosmos.Table;
-using NUnit.Framework.Constraints;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;

+ 0 - 13
TEAMModelOS.SDK/Models/Service/StudentService.cs

@@ -1,20 +1,8 @@
 using Azure;
 using Azure.Cosmos;
 using Azure.Messaging.ServiceBus;
-using DocumentFormat.OpenXml.Drawing;
-using DocumentFormat.OpenXml.Drawing.Charts;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Office2013.Excel;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.VariantTypes;
-using DocumentFormat.OpenXml.Vml;
 using HTEXLib.COMM.Helpers;
-using HTEXLib.Helpers.ShapeHelpers;
-using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
-using NUnit.Framework;
-using NUnit.Framework.Interfaces;
-using OpenXmlPowerTools;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -27,7 +15,6 @@ using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
-using TEAMModelOS.SDK.Models.Service;
 using Member = TEAMModelOS.SDK.Models.Member;
 
 namespace TEAMModelOS.SDK

+ 0 - 14
TEAMModelOS.SDK/Models/Service/SystemService.cs

@@ -1,35 +1,21 @@
 using Azure.Storage.Blobs.Models;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Drawing.Charts;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Wordprocessing;
-using HTEXLib;
 using HTEXLib.COMM.Helpers;
-using HTEXLib.PPTX.Models;
-using Microsoft.Azure.Amqp.Framing;
 using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
 using Newtonsoft.Json.Linq;
-using NUnit.Framework.Internal.Execution;
-using OpenXmlPowerTools;
 using StackExchange.Redis;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
-using System.Diagnostics.PerformanceData;
 using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
 using System.Net.Http;
 using System.Net.Http.Json;
-using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
-using TEAMModelOS.SDK.DI.Mail;
 using TEAMModelOS.SDK.Extension;
-using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.SDK.Models.Dtos;
 using static Azure.Core.HttpHeader;
 using static OpenXmlPowerTools.RevisionProcessor;

+ 24 - 28
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
-
 	<PropertyGroup>
-		<TargetFramework>net6.0</TargetFramework>
+		<TargetFramework>net8.0</TargetFramework>
 		<Version>5.2406.26</Version>
 		<AssemblyVersion>5.2406.26.1</AssemblyVersion>
 		<FileVersion>5.2406.26.1</FileVersion>
@@ -10,43 +9,40 @@
 
 	<ItemGroup>
 		<PackageReference Include="CHTCHSConv" Version="1.0.0" />
-		<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.2.0" />
+		<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
 		<PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
-		<PackageReference Include="Azure.Identity" Version="1.5.0" />
-		<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.7.0" />
-		<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.2.0" />
-		<PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.8.0" />
-		<PackageReference Include="Azure.Storage.Queues" Version="12.9.0" />
+		<!--<PackageReference Include="Azure.Identity" Version="1.12.0" />-->
+		<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.17.5" />
+		<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
+		<PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.17.0" />
+		<PackageReference Include="Azure.Storage.Queues" Version="12.18.0" />
 		<PackageReference Include="ClouDASLibx" Version="1.3.2" />
 		<PackageReference Include="DinkToPdf" Version="1.0.8" />
-		<PackageReference Include="DocumentFormat.OpenXml" Version="2.15.0" />
+		<PackageReference Include="DocumentFormat.OpenXml" Version="3.0.2" />
 		<PackageReference Include="HTEXLib" Version="5.2401.1024" />
-		<PackageReference Include="HtmlAgilityPack" Version="1.11.42" />
-		<PackageReference Include="Lib.AspNetCore.ServerSentEvents" Version="8.2.0" />
-		<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" />
+		<PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
+		<PackageReference Include="Lib.AspNetCore.ServerSentEvents" Version="9.0.0" />
+		<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
+		<!--<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="8.0.6" />-->
+		<!--<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="8.0.6" />-->
+		<PackageReference Include="System.Drawing.Common" Version="8.0.6" />
 		<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
-		<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.3" />
-		<PackageReference Include="Microsoft.Azure.SignalR.Management" Version="1.18.0" />
-		<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="7.0.5" />
-		<PackageReference Include="Microsoft.Identity.Client" Version="4.39.0" />
-		<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
-		<PackageReference Include="NUnit" Version="3.13.2" />
+		<PackageReference Include="Microsoft.Azure.SignalR.Management" Version="1.25.2" />
+		<!--<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />-->
+		<!--<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />-->
+		<!--<PackageReference Include="NUnit" Version="4.1.0" />-->
 		<PackageReference Include="PinYinConverterCore" Version="1.0.2" />
-		<PackageReference Include="StackExchange.Redis" Version="2.6.45" />
-		<PackageReference Include="SvgNet" Version="2.2.2" />
-		<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
+		<PackageReference Include="StackExchange.Redis" Version="2.8.0" />
+		<!--<PackageReference Include="SvgNet" Version="3.3.6" />-->
 		<PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="2.0.0-preview" />
-		<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
+		<!--<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />-->
 		<PackageReference Include="NPinyin.Core" Version="3.0.0" />
 		<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
 		<PackageReference Include="VueCliMiddleware" Version="6.0.0" />
-		<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
-		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Abstractions" Version="1.1.0" />
-		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
+		<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.22.0" />
+		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Abstractions" Version="1.3.0" />
+		<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
 	</ItemGroup>
-
-
-
 	<ItemGroup>
 		<Folder Include="DI\BBAPI\" />
 	</ItemGroup>

+ 2 - 2
TEAMModelOS.TEST/TEAMModelOS.TEST.csproj

@@ -1,8 +1,8 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
   </PropertyGroup>

+ 12 - 6
TEAMModelOS.sln

@@ -5,8 +5,6 @@ VisualStudioVersion = 17.0.32014.148
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TEAMModelOS.SDK", "TEAMModelOS.SDK\TEAMModelOS.SDK.csproj", "{E804B5FA-8D72-4ED4-AF9E-8AA48C17CE76}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TEAMModelOS.FunctionV4", "TEAMModelOS.FunctionV4\TEAMModelOS.FunctionV4.csproj", "{2A159D6A-55DB-4B0F-9129-3EB9EE28A1CC}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TEAMModelOS", "TEAMModelOS\TEAMModelOS.csproj", "{985D4ABD-281A-428C-81AD-60FCA0045DAD}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TEAMModelBI", "TEAMModelBI\TEAMModelBI.csproj", "{54DC5894-D5BA-40AB-9226-FB801E04BA24}"
@@ -19,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contest.Server", "TEAMModel
 EndProject
 Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "contest.client", "TEAMModelContest\contest.client\contest.client.esproj", "{84B6D998-91A7-420D-A409-4C7442B91055}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TEAMModelOS.Function", "TEAMModelOS.Function\TEAMModelOS.Function.csproj", "{1C7F42FE-4644-47C6-92BD-594BE22AB0C9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TEAMModelOS.FunctionV4", "TEAMModelOS.FunctionV4\TEAMModelOS.FunctionV4.csproj", "{537AD904-0079-4152-8BE0-1FABA04C1A16}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -29,10 +31,6 @@ Global
 		{E804B5FA-8D72-4ED4-AF9E-8AA48C17CE76}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E804B5FA-8D72-4ED4-AF9E-8AA48C17CE76}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E804B5FA-8D72-4ED4-AF9E-8AA48C17CE76}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2A159D6A-55DB-4B0F-9129-3EB9EE28A1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2A159D6A-55DB-4B0F-9129-3EB9EE28A1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2A159D6A-55DB-4B0F-9129-3EB9EE28A1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2A159D6A-55DB-4B0F-9129-3EB9EE28A1CC}.Release|Any CPU.Build.0 = Release|Any CPU
 		{985D4ABD-281A-428C-81AD-60FCA0045DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{985D4ABD-281A-428C-81AD-60FCA0045DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{985D4ABD-281A-428C-81AD-60FCA0045DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -55,6 +53,14 @@ Global
 		{84B6D998-91A7-420D-A409-4C7442B91055}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{84B6D998-91A7-420D-A409-4C7442B91055}.Release|Any CPU.Build.0 = Release|Any CPU
 		{84B6D998-91A7-420D-A409-4C7442B91055}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{1C7F42FE-4644-47C6-92BD-594BE22AB0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1C7F42FE-4644-47C6-92BD-594BE22AB0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1C7F42FE-4644-47C6-92BD-594BE22AB0C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1C7F42FE-4644-47C6-92BD-594BE22AB0C9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{537AD904-0079-4152-8BE0-1FABA04C1A16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{537AD904-0079-4152-8BE0-1FABA04C1A16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{537AD904-0079-4152-8BE0-1FABA04C1A16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{537AD904-0079-4152-8BE0-1FABA04C1A16}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 40
TEAMModelOS/Controllers/Both/ScoreCalcController.cs

@@ -3,69 +3,30 @@ using Azure.Core;
 using Azure.Cosmos;
 using Azure.Messaging.ServiceBus;
 using Azure.Storage.Blobs.Models;
-using DinkToPdf;
-using DinkToPdf.Contracts;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Office2016.Excel;
-using DocumentFormat.OpenXml.Presentation;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.VariantTypes;
-using DocumentFormat.OpenXml.Wordprocessing;
-using FastJSON;
+using DinkToPdf.Contracts; 
 using HTEXLib.COMM.Helpers;
-using HTEXLib.Helpers.ShapeHelpers;
-using MathNet.Numerics.Distributions;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
-using Microsoft.International.Converters.PinYinConverter;
-using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter;
-using Microsoft.OData.Edm;
 using Newtonsoft.Json;
-using NUnit.Framework;
-using NUnit.Framework.Internal;
-using OpenXmlPowerTools;
 using StackExchange.Redis;
 using System;
-using System.Collections;
-using System.Collections.Concurrent;
 using System.Collections.Generic;
-using System.IO;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
-using System.Net.Http.Json;
-using System.Runtime.Intrinsics.X86;
-using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
-using System.Text.RegularExpressions;
 using System.Threading.Tasks;
-using System.Xml.Linq;
-using TEAMModelOS.Controllers.Analysis;
-using TEAMModelOS.Controllers.Core;
-using TEAMModelOS.Filter;
 using TEAMModelOS.Models;
-using TEAMModelOS.Models.Dto;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
-using TEAMModelOS.SDK.Models.Cosmos.Common;
-using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
 using TEAMModelOS.SDK.Models.Cosmos.School;
-using TEAMModelOS.SDK.Models.Cosmos.Student;
 using TEAMModelOS.SDK.Models.Service;
-using TEAMModelOS.SDK.Models.Service.BI;
-using TEAMModelOS.SDK.Services;
-using static Azure.Core.HttpHeader;
-using static TEAMModelOS.Controllers.Learn.HomeworkController;
-using static TEAMModelOS.SDK.Models.Teacher;
-using static TEAMModelOS.SDK.SchoolService;
 
 namespace TEAMModelOS.Controllers
 {

+ 0 - 1
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -33,7 +33,6 @@ using Azure;
 using TEAMModelOS.Controllers.Both;
 using TEAMModelOS.SDK.Models.Cosmos.School;
 using Azure.Storage.Blobs;
-using Json.Path;
 using HtmlAgilityPack;
 using System.Diagnostics;
 using TEAMModelOS.Models.Service;

+ 1 - 22
TEAMModelOS/Controllers/Common/ArtController.cs

@@ -1,46 +1,25 @@
-using Azure.Core;
-using Azure.Cosmos;
+using Azure.Cosmos;
 using DinkToPdf.Contracts;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Office2013.Excel;
-using DocumentFormat.OpenXml.Office2016.Excel;
-using DocumentFormat.OpenXml.Presentation;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.Wordprocessing;
-using HTEXLib.COMM.Helpers;
-using HTEXLib.Helpers.ShapeHelpers;
-using MathNet.Numerics.Distributions;
-using MathNet.Numerics.RootFinding;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
-using NUnit.Framework;
-using OpenXmlPowerTools;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Net;
-using System.Reflection;
-using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
 using System.Web;
-using System.Xml.Linq;
-using TEAMModelOS.Controllers.Analysis;
 using TEAMModelOS.Filter;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
-using TEAMModelOS.SDK.Models.Cosmos;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.SDK.Models.Cosmos.Student;
-using Survey = TEAMModelOS.SDK.Models.Survey;
 
 namespace TEAMModelOS.Controllers.Common
 {

+ 1 - 10
TEAMModelOS/Controllers/School/ImportExamController.cs

@@ -1,22 +1,13 @@
-using DocumentFormat.OpenXml.Spreadsheet;
-using HTEXLib.COMM.Helpers;
-using Microsoft.AspNetCore.Authorization;
+using HTEXLib.COMM.Helpers;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.Amqp.Framing;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
-using NUnit.Framework;
 using OfficeOpenXml;
-using OpenXmlPowerTools;
-using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
-using System.ComponentModel;
 using System.Linq;
-using System.Runtime.Intrinsics.Arm;
-using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using TEAMModelOS.Filter;

+ 0 - 31
TEAMModelOS/Controllers/System/BillController.cs

@@ -1,53 +1,22 @@
 using Azure.Storage.Blobs.Models;
-using Azure.Storage.Blobs.Specialized;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Drawing;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.Wordprocessing;
-using FastJSON;
 using HTEXLib.COMM.Helpers;
-using IP2Region.Net.Abstractions;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Cors;
-using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.Amqp.Framing;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
-using Newtonsoft.Json.Linq;
-using NUnit.Framework;
-using OfficeOpenXml;
-using OpenXmlPowerTools;
-using StackExchange.Redis;
 using System;
-using System.Collections;
-using System.Collections.Concurrent;
 using System.Collections.Generic;
-using System.ComponentModel;
 using System.IdentityModel.Tokens.Jwt;
-using System.IO;
 using System.Linq;
 using System.Net.Http;
-using System.Runtime.Intrinsics.Arm;
-using System.Text;
 using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Text.RegularExpressions;
-using System.Threading;
 using System.Threading.Tasks;
-using System.Web;
-using TEAMModelOS.Filter;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
-using TEAMModelOS.SDK.IP2Region;
-using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
-using TEAMModelOS.SDK.Models.Service;
-using Top.Api;
-using static TEAMModelOS.SDK.Models.Service.SystemService;
 
 namespace TEAMModelOS.Controllers
 {

+ 0 - 36
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -1,34 +1,14 @@
 using Azure;
-using Azure.Core;
 using Azure.Cosmos;
-using Azure.Messaging.ServiceBus;
-using Azure.Messaging.ServiceBus.Administration;
-using Azure.Storage.Blobs;
 using Azure.Storage.Blobs.Models;
-using Azure.Storage.Blobs.Specialized;
 using DinkToPdf;
 using DinkToPdf.Contracts; 
-using DocumentFormat.OpenXml.Drawing.Wordprocessing;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Office2016.Excel;
-using DocumentFormat.OpenXml.Presentation;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.Wordprocessing;
-using FastJSON;
 using HTEXLib.COMM.Helpers;
-using HTEXLib.Helpers.ShapeHelpers;
-using Json.Path;
-using MathNet.Numerics.Distributions;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
-using Microsoft.International.Converters.PinYinConverter;
-using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter;
-using Newtonsoft.Json;
-using OpenXmlPowerTools;
-using StackExchange.Redis;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -37,20 +17,11 @@ using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http.Json;
-using System.Runtime.Intrinsics.X86;
-using System.Runtime.Serialization;
-using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Text.RegularExpressions;
 using System.Threading.Tasks;
-using System.Web;
-using TEAMModelOS.Controllers.Analysis;
-using TEAMModelOS.Controllers.Core;
 using TEAMModelOS.Controllers.Third.LePei;
 using TEAMModelOS.Filter;
-using TEAMModelOS.Helper.Common.FileHelper;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
@@ -58,15 +29,8 @@ using TEAMModelOS.SDK.DI.Mail;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
-using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
-using TEAMModelOS.SDK.Models.Cosmos.School;
-using TEAMModelOS.SDK.Models.Dtos;
 using TEAMModelOS.SDK.Models.Service;
-using TEAMModelOS.SDK.Models.Service.BI;
 using TEAMModelOS.SDK.Services;
-using Top.Api;
-using static SKIT.FlurlHttpClient.Wechat.TenpayV3.Models.CreateBrandProfitSharingOrderRequest.Types;
-using static TEAMModelOS.Controllers.FixDataController;
 using static TEAMModelOS.SDK.Models.Teacher;
 using static TEAMModelOS.SDK.SchoolService;
 

+ 1 - 11
TEAMModelOS/Filter/RequestAuditFilter.cs

@@ -1,28 +1,18 @@
-using Microsoft.AspNetCore.Mvc.Controllers;
-using Microsoft.AspNetCore.Mvc.Filters;
-using System.Security.Claims;
+using Microsoft.AspNetCore.Mvc.Filters;
 using System;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.Extension;
-using Microsoft.Extensions.Logging;
 using TEAMModelOS.SDK;
-using DocumentFormat.OpenXml.Office2010.Excel;
-using DocumentFormat.OpenXml.Wordprocessing;
 using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
-using Azure.Core;
-using DocumentFormat.OpenXml.Office2016.Excel;
 using TEAMModelOS.SDK.DI;
 using Microsoft.Extensions.Primitives;
 using HTEXLib.Helpers.ShapeHelpers;
-using TEAMModelOS.Controllers;
-using System.Net;
 using System.Net.Http;
 using System.Net.Http.Json;
 using TEAMModelOS.Models;
 using Microsoft.Extensions.Options;
 using System.Collections.Generic;
-using FastJSON;
 
 namespace TEAMModelOS.Filter
 {

+ 5 - 5
TEAMModelOS/TEAMModelOS.csproj

@@ -1,17 +1,17 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 	<PropertyGroup>
-		<TargetFramework>net6.0</TargetFramework>
+		<TargetFramework>net8.0</TargetFramework>
 	</PropertyGroup>
 	<ItemGroup>
 		<PackageReference Include="DotNetZip" Version="1.16.0" />
 		<PackageReference Include="DinkToPdf" Version="1.0.8" />
-		<PackageReference Include="EPPlus" Version="6.2.6" />
+		<PackageReference Include="EPPlus" Version="7.2.0" />
 		<PackageReference Include="IP2Region.Net" Version="2.0.2" />
-		<PackageReference Include="JsonPath.Net" Version="1.0.0" />
-		<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
+		<!--<PackageReference Include="JsonPath.Net" Version="1.1.2" />-->
+		<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
 		<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="2.20.0" />
-		<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
+		<!--<PackageReference Include="System.Security.Cryptography.Algorithms" Version="3.5.0" />-->
 	</ItemGroup>
 	<ItemGroup>
 		<None Remove="ClientApp\src\static\BaseDataDefault.json" />