123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- using Azure.Cosmos;
- using Azure.Storage.Blobs.Models;
- using Azure.Storage.Sas;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Options;
- using System;
- using System.Collections.Generic;
- using System.Dynamic;
- using System.IdentityModel.Tokens.Jwt;
- using System.IO;
- using System.Linq;
- using System.Text.Json;
- using System.Threading.Tasks;
- using TEAMModelOS.Models;
- using TEAMModelOS.SDK.Models;
- using TEAMModelOS.SDK.DI;
- using TEAMModelOS.SDK.Extension;
- namespace TEAMModelOS.Controllers
- {
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- //[Authorize(Roles = "IES5")]
- [Route("teacher/init")]
- [ApiController]
- public class InitController : ControllerBase
- {
- private readonly AzureCosmosFactory _azureCosmos;
- private readonly AzureStorageFactory _azureStorage;
- private readonly DingDing _dingDing;
- private readonly Option _option;
- public InitController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option)
- {
- _azureCosmos = azureCosmos;
- _azureStorage = azureStorage;
- _dingDing = dingDing;
- _option = option?.Value;
- }
- //TODO 此API需處理對應前端返回的相關數據
- [ProducesDefaultResponseType]
- [HttpPost("get-teacher-info")]
- public async Task<IActionResult> GetTeacherInfo(JsonElement request)
- {
- //Debug
- //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
- try
- {
- if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
- var jwt = new JwtSecurityToken(id_token.GetString());
- //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
- if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
- var id = jwt.Payload.Sub;
- jwt.Payload.TryGetValue("name", out object name);
- jwt.Payload.TryGetValue("picture", out object picture);
- object schools = null;
- string defaultschool = null;
- //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
- var client = _azureCosmos.GetCosmosClient();
- var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
- //老師個人資料(含初始化)
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- if (json.RootElement.TryGetProperty("schools", out JsonElement value))
- {
- schools = value.ToObject<object>();
- }
- //預設學校ID
- if (json.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
- {
- defaultschool = valueD.ToString();
- }
- }
- else
- {
- //如果沒有,則初始化Teacher基本資料到Cosmos
- using var stream = new MemoryStream();
- using var writer = new Utf8JsonWriter(stream); //new JsonWriterOptions() { Indented = true }
- writer.WriteStartObject();
- writer.WriteString("pk", "Base");
- writer.WriteString("code", "Base");
- writer.WriteString("id", id);
- writer.WriteString("name", name?.ToString());
- writer.WriteString("picture", picture?.ToString());
- writer.WriteNumber("size", 1);
- writer.WriteNull("defaultSchool");
- writer.WriteStartArray("schools");
- writer.WriteEndArray();
- writer.WriteEndObject();
- writer.Flush();
- //Debug
- //string teacher = Encoding.UTF8.GetString(stream.ToArray());
- response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
- }
- //私人課程
- List<object> courses = new List<object>();
- await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
- {
- 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())
- {
- courses.Add(obj.ToObject<object>());
- }
- }
- }
- //老師個人課綱
- List<SyllabusRole> syllabus = new List<SyllabusRole>();
- await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{id}") }))
- {
- var jsons = await JsonDocument.ParseAsync(item.ContentStream);
- if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
- {
- foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
- {
- SyllabusRole syllabusRole = new SyllabusRole();
- syllabusRole.id = obj.GetProperty("id").ToString();
- syllabusRole.name = obj.GetProperty("name").ToString();
- syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
- syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
- List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
- syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
- syllabusList = syllabusList.OrderBy(x => x.order).ToList();
- syllabusRole.structure = CreateSyllabusTree(syllabusList);
- syllabus.Add(syllabusRole);
- }
- //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
- }
- }
- //換取AuthToken,提供給前端
- var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher", "student" });
- //取得Teacher Blob 容器位置及SAS
- var container = _azureStorage.GetBlobContainerClient(id);
- await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
- var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
- return Ok(new { auth_token, blob_uri, blob_sas, schools, defaultschool, courses });
- }
- catch (Exception ex)
- {
- await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
- }
- //TODO 此API需處理對應前端返回的相關數據
- [ProducesDefaultResponseType]
- [HttpPost("get-school-info")]
- public async Task<IActionResult> GetSchoolInfo(JsonElement requert)
- {
- if (!requert.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
- if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
- var jwt = new JwtSecurityToken(id_token.GetString());
- if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
- var id = jwt.Payload.Sub;
- var client = _azureCosmos.GetCosmosClient();
- //權限token
- jwt.Payload.TryGetValue("name", out object name);
- jwt.Payload.TryGetValue("picture", out object picture);
- List<string> roles = new List<string>();
- List<string> permissions = new List<string>();
-
- var response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- if (json.RootElement.TryGetProperty("roles", out JsonElement _roles) && _roles.ValueKind != JsonValueKind.Null)
- {
- foreach (var obj in _roles.EnumerateArray())
- {
- roles.Add(obj.GetString());
- }
- }
- if (json.RootElement.TryGetProperty("permissions", out JsonElement _permissions) && _permissions.ValueKind != JsonValueKind.Null)
- {
- foreach (var obj in _permissions.EnumerateArray())
- {
- permissions.Add(obj.GetString());
- }
- }
- }
- else //無此學校資料
- {
- return BadRequest();
- }
- if (roles.Count == 0)
- {
- roles.Add("teacher");
- roles.Add("student");
- }
- //TODO JJ,更新Token时,在取得学校资讯时,没有传入schoolId
- var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, schoolID: school_code.ToString(), roles: roles.ToArray(), permissions: permissions.ToArray());
- //TODO JJ,调整为取得学校基础设置数据,取代下方學校學制、年級
- object school_base = null;
- response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey("Base"));
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- school_base = json.RootElement.ToObject<object>();
- }
- //取得教室
- List<object> school_classes = new List<object>();
- await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id,c.x,c.y,c.name,c.teacher,c.periodId,c.gradeId,c.sn,c.no,c.style,c.status,c.openType,c.scope, ARRAY_LENGTH(c.students) AS studCount FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
- {
- var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
- foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
- {
- school_classes.Add(classeinfo.ToObject<object>());
- }
- }
- //List<object> periods = new List<object>();
- //List<object> grades = new List<object>();
- //var responsesch = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
- //if (responsesch.Status == 200)
- //{
- // var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
- // if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
- // {
- // foreach (var periodinfo in periodJobj.EnumerateArray())
- // {
- // dynamic periodExtobj = new ExpandoObject();
- // periodExtobj.id = periodinfo.GetProperty("id");
- // periodExtobj.name = periodinfo.GetProperty("name");
- // periods.Add(periodExtobj);
- // if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
- // {
- // foreach (var gradeinfo in gradesJobj.EnumerateArray())
- // {
- // dynamic gradeExtobj = new ExpandoObject();
- // gradeExtobj.id = gradeinfo.GetProperty("id");
- // gradeExtobj.name = gradeinfo.GetProperty("name");
- // gradeExtobj.periodId = periodinfo.GetProperty("id");
- // grades.Add(gradeExtobj);
- // }
- // }
- // }
- // }
- //}
- //該老師排定的學校課程
- List<object> school_courses = new List<object>();
- var query = $"SELECT c.id, c.name, c.teacher, cc.course, c.scope FROM c JOIN cc IN c.courses JOIN cct IN cc.teachers WHERE cct.id = '{id}'";
- await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
- {
- var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
- if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
- {
- foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
- {
- //班級
- string classIdNow = obj.GetProperty("id").ToString();
- dynamic classExtobj = new ExpandoObject();
- classExtobj.code = $"Class-{school_code}";
- classExtobj.id = classIdNow;
- classExtobj.name = obj.GetProperty("name").ToString();
- classExtobj.teacher = obj.GetProperty("teacher").ToObject<object>();
- classExtobj.scope = obj.GetProperty("scope").ToString();
- //課程
- obj.TryGetProperty("course", out JsonElement courseNow);
- string courseIdNow = courseNow.GetProperty("id").ToString();
- var courseExist = school_courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
- if (courseExist == null)
- {
- dynamic courseExtobj = new ExpandoObject();
- courseExtobj.id = courseIdNow;
- courseExtobj.name = courseNow.GetProperty("name").ToString();
- courseExtobj.scope = courseNow.GetProperty("scope").ToString();
- courseExtobj.classes = new List<object>();
- courseExtobj.classes.Add(classExtobj);
- school_courses.Add(courseExtobj);
- }
- else
- {
- courseExist.classes.Add(classExtobj);
- }
- }
- }
- }
- //校本課綱 [式樣未定 先不取]
- List<SyllabusRole> school_syllabus = new List<SyllabusRole>();
- //await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
- //{
- // var jsons = await JsonDocument.ParseAsync(item.ContentStream);
- // if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
- // {
- // foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
- // {
- // SyllabusRole syllabusRole = new SyllabusRole();
- // syllabusRole.id = obj.GetProperty("id").ToString();
- // syllabusRole.name = obj.GetProperty("name").ToString();
- // syllabusRole.period = obj.GetProperty("period");
- // syllabusRole.grade = obj.GetProperty("grade");
- // syllabusRole.semester = obj.GetProperty("semester");
- // syllabusRole.subject = obj.GetProperty("subject");
- // syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
- // syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
- // List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
- // syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
- // syllabusList = syllabusList.OrderBy(x => x.order).ToList();
- // syllabusRole.structure = CreateSyllabusTree(syllabusList);
- // syllabus.Add(syllabusRole);
- // }
- // //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
- // }
- //}
- //取得School Blob 容器位置及SAS
- string school_code_blob = school_code.GetString().ToLower();
- var container = _azureStorage.GetBlobContainerClient(school_code_blob);
- await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建School容器,如存在則不做任何事,保障容器一定存在
- var (blob_uri, blob_sas) = (roles.Contains("admin")) ? _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List) : _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
- return Ok(new { auth_token, blob_uri, blob_sas, school_base, school_courses, school_syllabus, school_classes });
- }
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "teacher")]
- [HttpPost("get-school-list")]
- public async Task<IActionResult> GetSchoolList()
- {
- var client = _azureCosmos.GetCosmosClient();
- List<object> schools = new List<object>();
- await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"select c.id, c.name,c.region,c.province,c.city from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
- {
- 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())
- {
- schools.Add(obj.ToObject<object>());
- }
- }
- }
- return Ok(new { schools });
- }
- /// <summary>
- /// 申請或同意邀請加入學校
- /// </summary>
- ///
- /// <param name="requert"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "teacher")]
- [HttpPost("join-school")]
- public async Task<IActionResult> JoinSchool(JsonElement requert)
- {
- try
- {
- if (!requert.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest(); //"invite":學校邀請 "request":老師申請 "join":"成為學校老師"
- if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
- if (!requert.TryGetProperty("school_name", out JsonElement school_name)) return BadRequest();
- string authtoken = HttpContext.GetXAuth("AuthToken");
- if (string.IsNullOrEmpty(authtoken)) return BadRequest();
- var jwt = new JwtSecurityToken(authtoken);
- var id = jwt.Payload.Sub;
- var schoolcode = jwt.Payload.Azp;
- var Claims = jwt.Payload.Claims;
- jwt.Payload.TryGetValue("name", out object name);
- jwt.Payload.TryGetValue("picture", out object picture);
- var client = _azureCosmos.GetCosmosClient();
- //在老師表找出老師,處理該學校狀態 (老師基本資料應該要存在)
- Teacher teacher = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
- if (teacher.schools == null)
- teacher.schools = new List<Teacher.School>();
- var school = teacher.schools?.FirstOrDefault(x => x.schoolId.Equals(school_code.GetString(), StringComparison.OrdinalIgnoreCase));
- if (school != null)
- school.status = grant_type.GetString();
- else
- teacher.schools.Add(new Teacher.School() { schoolId = school_code.GetString(), name = school_name.GetString(), status = grant_type.GetString() });
- await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
- //在學校表處理該學校教師帳號的狀態
- var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
- if (sresponse.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
- SchoolTeacher steacher = json.ToObject<SchoolTeacher>();
- steacher.status = grant_type.GetString();
- var response = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(steacher, id, new PartitionKey($"Teacher-{school_code}"));
- }
- else
- {
- SchoolTeacher st = new SchoolTeacher()
- {
- pk = "Teacher",
- code = $"Teacher-{school_code}",
- createTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
- id = id,
- name = name.ToString(),
- picture = picture?.ToString(), //TODO JJ,原本的判断会出现报错,没检查null却又去ToString
- permissions = new List<string>(),
- roles = new List<string>(){ "teacher" },
- status = grant_type.GetString()
- };
- var response = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(st, new PartitionKey($"Teacher-{school_code}"));
- }
- return Ok();
- }
- catch (Exception ex)
- {
- await _dingDing.SendBotMsg($"TEAMModel,{_option.Location},Init/JoinSchool()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
- }
- //課綱的model先記在下面,待式樣確定後再轉換
- private List<SyllabusNode> CreateSyllabusTree(List<Syllabus> syllabuses)
- {
- List<SyllabusNode> nodes = new List<SyllabusNode>();
- foreach (var syllabus in syllabuses)
- {
- if (syllabus.pid == "")
- nodes.Add(new SyllabusNode { id = syllabus.id, name = syllabus.name });
- else
- {
- CreateNode(nodes, syllabus);
- }
- }
- return nodes;
- }
- private void CreateNode(List<SyllabusNode> nodes, Syllabus parent)
- {
- foreach (var node in nodes)
- {
- if (node.id == parent.pid)
- {
- node.children.Add(new SyllabusNode { id = parent.id, name = parent.name });
- }
- else
- {
- CreateNode(node.children, parent);
- }
- }
- }
- public class SyllabusRole
- {
- public string id { get; set; }
- public string name { get; set; }
- public object period { get; set; }
- public object semester { get; set; }
- public object grade { get; set; }
- public object subject { get; set; }
- public int resourceCount { get; set; }
- public int itemCount { get; set; }
- public object structure { get; set; }
- }
- public class Syllabus
- {
- public string id { get; set; }
- public string name { get; set; }
- public string pid { get; set; }
- public int order { get; set; }
- }
- public class SyllabusNode
- {
- public string id { get; set; }
- public string name { get; set; }
- public List<SyllabusNode> children { get; set; }
- public SyllabusNode()
- {
- children = new List<SyllabusNode>();
- }
- }
- }
- }
|