StudentController.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. using Azure.Cosmos;
  2. using Microsoft.AspNetCore.Mvc;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using TEAMModelOS.SDK;
  8. using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
  9. using TEAMModelOS.SDK.Helper.Security.TmdCrypt;
  10. using TEAMModelOS.SDK.DI;
  11. using TEAMModelOS.SDK.Helper.Common.ValidateHelper;
  12. using TEAMModelOS.Models;
  13. using System.Text.Json;
  14. using TEAMModelOS.Models.StudentInfo;
  15. using Microsoft.AspNetCore.Http;
  16. using TEAMModelOS.SDK.Extension;
  17. using Microsoft.AspNetCore.Cryptography.KeyDerivation;
  18. using System.Text;
  19. using Microsoft.Extensions.Options;
  20. namespace TEAMModelOS.Controllers
  21. {
  22. [ProducesResponseType(StatusCodes.Status200OK)]
  23. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  24. //[Authorize(Roles = "IES5")]
  25. [Route("student/init")]
  26. [ApiController]
  27. // [Authorize]
  28. public class StudentController : ControllerBase
  29. {
  30. private readonly AzureCosmosFactory _azureCosmos;
  31. private readonly Option _option;
  32. public StudentController(AzureCosmosFactory azureCosmos, IOptionsSnapshot<Option> option)
  33. {
  34. _azureCosmos = azureCosmos;
  35. _option = option?.Value;
  36. }
  37. /// <summary>
  38. /// 保存或更新学生,并维护学生关系表
  39. /// </summary>
  40. /// <param name="request"></param>
  41. /// <returns></returns>
  42. [ProducesDefaultResponseType]
  43. //[AuthToken(Roles = "teacher")]
  44. [HttpPost("upsert")]
  45. public async Task<BaseResponse> Upsert(Student request)
  46. {
  47. ResponseBuilder builder = ResponseBuilder.custom();
  48. //设置密码 isSet 是否加密 如果加密则不会再次加密
  49. if (!request.password.isSet)
  50. {
  51. request.password.value = TmdCrypt.Encrypt(request.password.value);
  52. request.password.isSet = true;
  53. }
  54. request.id = request.studentId.Replace("#", "-");
  55. ///假如更新了班级则先获取更新之前的班级
  56. var olStudent= await _azureCosmos.FindByIdPk<Student>(request.id,request.code);
  57. if (olStudent!=null && !string.IsNullOrEmpty(olStudent.classroomCode) && ! olStudent.classroomCode.Equals(request.classroomCode) ) {
  58. //移除之前的原生班级
  59. IdPk idPk=await _azureCosmos.DeleteAsync<ClassStudent>( olStudent.classroomCode ,olStudent.studentId);
  60. }
  61. ///新建最新的班级关系表
  62. ClassStudent classroomStudent = new ClassStudent { id = request.classroomCode, code = request.studentId };
  63. await _azureCosmos.SaveOrUpdate(classroomStudent);
  64. Student data = await _azureCosmos.SaveOrUpdate<Student>(request);
  65. return builder.Data(data).build();
  66. }
  67. /// <summary>
  68. /// 查找学生
  69. /// </summary>
  70. /// <param name="request"></param>
  71. /// <returns></returns>
  72. [ProducesDefaultResponseType]
  73. //[AuthToken(Roles = "teacher")]
  74. [HttpPost("find")]
  75. public async Task<IActionResult> Find(JsonElement requert)
  76. {
  77. var client = _azureCosmos.GetCosmosClient();
  78. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  79. List<object> students = new List<object>();
  80. await foreach (var item in client.GetContainer("TEAMModelOS", "Student").GetItemQueryStreamIterator(queryText: $"select c.id, c.name,c.mail,c.mobile,c.year,c.schoolId from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Student-{school_code}") }))
  81. {
  82. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  83. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  84. {
  85. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  86. {
  87. students.Add(obj.ToObject<object>());
  88. }
  89. }
  90. }
  91. return Ok(new { students });
  92. /* ResponseBuilder builder = ResponseBuilder.custom();
  93. if (request.TryGetProperty("code", out _))
  94. {
  95. List<Student> data = await _azureCosmos.FindByDict<Student>(request);
  96. return builder.Data(data).build();
  97. }
  98. else
  99. {
  100. return builder.Error(ResponseCode.PARAMS_ERROR, "code is null !").build();
  101. }*/
  102. }
  103. [ProducesDefaultResponseType]
  104. //[AuthToken(Roles = "teacher")]
  105. [HttpPost("upsert-all")]
  106. public async Task<IActionResult> UpsertAll(List<Student> request)
  107. {
  108. ResponseBuilder builder = ResponseBuilder.custom();
  109. if (ValidateHelper.IsValid(request) && request.IsNotEmpty()) {
  110. ///假如更新了班级则先获取更新之前的班级
  111. string[] ids = request.Select(x=>x.studentId).ToArray();
  112. List<Student> oldStudent = await _azureCosmos.FindByDict<Student>(new Dictionary<string, object>() { { "studentId", ids } });
  113. List<IdPk> idPks = new List<IdPk>();
  114. ///处理未变动的班级关系
  115. List<IdPk> unpk = new List<IdPk>();
  116. oldStudent.ForEach(x=> {
  117. request.ForEach(m => {
  118. if (x.studentId.Equals(m.studentId)) {
  119. if (!x.classroomCode.Equals(m.classroomCode))
  120. {
  121. idPks.Add(new IdPk { id = x.classroomCode, pk = x.studentId });
  122. }
  123. else {
  124. unpk.Add(new IdPk { id = x.classroomCode, pk = x.studentId });
  125. }
  126. }
  127. });
  128. });
  129. if (idPks.IsNotEmpty()) {
  130. await _azureCosmos.DeleteAll<ClassStudent>(idPks);
  131. }
  132. long createDate = DateTimeOffset.UtcNow.Ticks;
  133. request.ForEach(
  134. x => {
  135. x.createDate = createDate;
  136. x.id = x.studentId.Replace("#", "-");
  137. //设置密码 isSet 是否加密 如果加密则不会再次加密
  138. if (!x.password.isSet)
  139. {
  140. x.password.value = TmdCrypt.Encrypt(x.password.value);
  141. x.password.isSet = true;
  142. }
  143. });
  144. List<Student> students = await _azureCosmos.SaveOrUpdateAll(request);
  145. ///更新学生关系表
  146. List<ClassStudent> classroomStudents = new List<ClassStudent>();
  147. foreach (var student in students)
  148. {
  149. // 处理未变更原生班级的学生
  150. bool has = false;
  151. foreach (IdPk idPk in unpk) {
  152. if (idPk.id.Equals(student.classroomCode) && idPk.pk.Equals(student.studentId)) {
  153. has = true;
  154. }
  155. }
  156. if (has)
  157. {
  158. continue;
  159. }
  160. else {
  161. classroomStudents.Add(new ClassStudent { id = student.classroomCode, code = student.studentId });
  162. }
  163. }
  164. await _azureCosmos.SaveOrUpdateAll(classroomStudents);
  165. builder.Data(students);
  166. }
  167. return Ok();
  168. }
  169. /// <summary>
  170. /// 删除单个学生
  171. /// </summary>
  172. /// <param name="request"></param>
  173. /// <returns></returns>
  174. [ProducesDefaultResponseType]
  175. //[AuthToken(Roles = "teacher")]
  176. [HttpPost("delete")]
  177. public async Task<IActionResult> Delete(JsonElement requert)
  178. {
  179. if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
  180. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  181. var client = _azureCosmos.GetCosmosClient();
  182. var response = await client.GetContainer("TEAMModelOS", "Student").DeleteItemStreamAsync(id.ToJsonString(), new PartitionKey($"Base-{school_code}"));
  183. if (response.Status == 200)
  184. {
  185. await _azureCosmos.DeleteAll<ClassStudent>(new Dictionary<string, object> { { "code", id.ToJsonString() } });
  186. }
  187. else {
  188. return BadRequest();
  189. }
  190. return Ok();
  191. /*ResponseBuilder builder = ResponseBuilder.custom();
  192. IdPk data = await _azureCosmos.DeleteAsync<Student>(request.id, request.code);
  193. ///更新学生关系表
  194. await _azureCosmos.DeleteAll<ClassStudent>(new Dictionary<string, object> { { "code", request.studentId } });
  195. return builder.Data(data).build();*/
  196. }
  197. /// <summary>
  198. /// 批量删除并维护关联关系
  199. /// </summary>
  200. /// <param name="request"></param>
  201. /// <returns></returns>
  202. [ProducesDefaultResponseType]
  203. //[AuthToken(Roles = "teacher")]
  204. [HttpPost("bulkDelete")]
  205. public async Task<IActionResult> BulkDelete(JsonElement request)
  206. {
  207. ResponseBuilder builder = ResponseBuilder.custom();
  208. //Dictionary<string, object> dict = new Dictionary<string, object>();
  209. var emobj = request.EnumerateObject();
  210. int keys = 0;
  211. while (emobj.MoveNext())
  212. {
  213. keys++;
  214. //dict[emobj.Current.Name] = emobj.Current.Value;
  215. }
  216. if (keys > 0&& request.TryGetProperty("code",out JsonElement code)) {
  217. List<Student> students = await _azureCosmos.FindByDict<Student>(request);
  218. await _azureCosmos.DeleteAll<Student>(students);
  219. ///更新学生关系表
  220. await _azureCosmos.DeleteAll<ClassStudent>(new Dictionary<string, object> { {"code",students.Select(x=>x.studentId).ToArray() } });
  221. builder.Data(students);
  222. }
  223. return Ok();
  224. }
  225. /// <summary>
  226. /// 學生登入
  227. /// </summary>
  228. /// <param name = "request" ></ param >
  229. [HttpPost("Login")]
  230. public async Task<IActionResult> Login(JsonElement request)
  231. {
  232. var client = _azureCosmos.GetCosmosClient();
  233. //參數取得
  234. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  235. if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
  236. if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
  237. var response = await client.GetContainer("TEAMModelOS", "Student").ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.ToString().ToLower()}"));
  238. int error = 0;
  239. string message = "帳號或密碼錯誤";
  240. string auth_token = "";
  241. if (response.Status == 200)
  242. {
  243. using var json = await JsonDocument.ParseAsync(response.ContentStream);
  244. // 取得資料庫salt
  245. json.RootElement.TryGetProperty("salt", out JsonElement salt);
  246. // 取得資料庫pw
  247. json.RootElement.TryGetProperty("pw", out JsonElement dbpw);
  248. // 取得資料庫name
  249. json.RootElement.TryGetProperty("name", out JsonElement name);
  250. var HashedPW = HashedPassword(pw.ToString(), salt.ToString());
  251. if (dbpw.ToString().Equals(HashedPW.ToString()))
  252. {
  253. //換取AuthToken,提供給前端
  254. auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id.GetString(), name.GetString(), "", _option.JwtSecretKey, roles: new[] { "student" });
  255. }
  256. else
  257. {
  258. error = 1;
  259. }
  260. }
  261. else
  262. {
  263. error = 1;
  264. }
  265. if (error > 0)
  266. {
  267. return Ok(
  268. new
  269. {
  270. error,
  271. message
  272. }
  273. );
  274. }
  275. else
  276. {
  277. return Ok(
  278. new
  279. {
  280. auth_token
  281. }
  282. );
  283. }
  284. }
  285. public static string HashedPassword(string password, string salt)
  286. {
  287. byte[] hashBytes = KeyDerivation.Pbkdf2(
  288. password: password,
  289. salt: Encoding.UTF8.GetBytes(salt), // SHA1鹽(8-20字節), SHA256(32字節)
  290. prf: KeyDerivationPrf.HMACSHA1,
  291. iterationCount: 10000, // hash次數,越多次代表破解難度變高,但效能差點
  292. numBytesRequested: 256 / 8 // 指定得出結果長度
  293. );
  294. String hashText = BitConverter.ToString(hashBytes).Replace("-", string.Empty);
  295. return hashText;
  296. }
  297. }
  298. }