|
@@ -0,0 +1,443 @@
|
|
|
+using Azure.Cosmos;
|
|
|
+using Microsoft.AspNetCore.Http;
|
|
|
+using Microsoft.AspNetCore.Mvc;
|
|
|
+using Microsoft.Extensions.Configuration;
|
|
|
+using Microsoft.Extensions.Options;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Net;
|
|
|
+using System.Text.Json;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using TEAMModelOS.Filter;
|
|
|
+using TEAMModelOS.Models;
|
|
|
+using TEAMModelOS.SDK.DI;
|
|
|
+using TEAMModelOS.SDK.Extension;
|
|
|
+using TEAMModelOS.SDK.Models.Cosmos.School;
|
|
|
+
|
|
|
+namespace TEAMModelOS.Controllers.Common
|
|
|
+{
|
|
|
+ [ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
|
+ //[Authorize(Roles = "IES5")
|
|
|
+ [Route("common/study")]
|
|
|
+ [ApiController]
|
|
|
+ public class StudyController : ControllerBase
|
|
|
+ {
|
|
|
+ private readonly AzureCosmosFactory _azureCosmos;
|
|
|
+ private readonly SnowflakeId _snowflakeId;
|
|
|
+ private readonly AzureServiceBusFactory _serviceBus;
|
|
|
+ private readonly DingDing _dingDing;
|
|
|
+ private readonly Option _option;
|
|
|
+ private readonly AzureStorageFactory _azureStorage;
|
|
|
+ private readonly AzureRedisFactory _azureRedis;
|
|
|
+ public IConfiguration _configuration { get; set; }
|
|
|
+ public StudyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing,
|
|
|
+ IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, IConfiguration configuration)
|
|
|
+ {
|
|
|
+ _azureCosmos = azureCosmos;
|
|
|
+ _serviceBus = serviceBus;
|
|
|
+ _snowflakeId = snowflakeId;
|
|
|
+ _dingDing = dingDing;
|
|
|
+ _option = option?.Value;
|
|
|
+ _azureStorage = azureStorage;
|
|
|
+ _azureRedis = azureRedis;
|
|
|
+ _configuration = configuration;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// 保存研修信息
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="request"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ [AuthToken(Roles = "teacher,admin")]
|
|
|
+ [HttpPost("save")]
|
|
|
+ public async Task<IActionResult> Save(JsonElement request)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ Study study = request.ToObject<Study>();
|
|
|
+ string code = study.school;
|
|
|
+ study.ttl = -1;
|
|
|
+ study.owner = "school";
|
|
|
+ study.code = "Study-" + code;
|
|
|
+ long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
|
+ study.createTime = now;
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(study.id))
|
|
|
+ {
|
|
|
+ study.id = Guid.NewGuid().ToString();
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(study, new PartitionKey($"{study.code}"));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(study, study.id, new PartitionKey($"{study.code}"));
|
|
|
+ }
|
|
|
+ return Ok(new { study });
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/save()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 签到
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="request"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "teacher,admin")]
|
|
|
+ [HttpPost("sign-in")]
|
|
|
+ public async Task<IActionResult> Sign(JsonElement request)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/sign-in()\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
|
+ var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(id.ToString(), new PartitionKey($"Study-{code}"));
|
|
|
+ if (response.Status == (int)HttpStatusCode.OK)
|
|
|
+ {
|
|
|
+ var json = await JsonDocument.ParseAsync(response.ContentStream);
|
|
|
+ Study study = json.ToObject<Study>();
|
|
|
+ //bool flag = false;
|
|
|
+ bool flag = study.teachers.Exists(s => s.id.Equals(tId.GetString()) && string.IsNullOrEmpty(s.sign));
|
|
|
+ if (flag) {
|
|
|
+ if (study.detail.startTime < now)
|
|
|
+ {
|
|
|
+ foreach (Setting setting in study.teachers)
|
|
|
+ {
|
|
|
+ if (setting.id.Equals(tId.GetString()))
|
|
|
+ {
|
|
|
+ setting.sign = "2";
|
|
|
+ setting.signTime = now;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ foreach (Setting setting in study.teachers)
|
|
|
+ {
|
|
|
+ if (setting.id.Equals(tId.GetString()))
|
|
|
+ {
|
|
|
+ setting.sign = "1";
|
|
|
+ setting.signTime = now;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(study, study.id, new PartitionKey($"{study.code}"));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return Ok(new { code = HttpStatusCode.NotFound});
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/sign-in()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 上传作业
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="request"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ [AuthToken(Roles = "teacher,admin")]
|
|
|
+ [HttpPost("upload")]
|
|
|
+ public async Task<IActionResult> Upload(JsonElement request)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("hw", out JsonElement hw)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
|
+ var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(id.ToString(), new PartitionKey($"Study-{code}"));
|
|
|
+ if (response.Status == (int)HttpStatusCode.OK)
|
|
|
+ {
|
|
|
+ var json = await JsonDocument.ParseAsync(response.ContentStream);
|
|
|
+ Study study = json.ToObject<Study>();
|
|
|
+ bool flag = study.teachers.Exists(s => s.id.Equals(tId.GetString()));
|
|
|
+ if (flag)
|
|
|
+ {
|
|
|
+ foreach (Setting setting in study.teachers)
|
|
|
+ {
|
|
|
+ if (setting.id.Equals(tId.GetString()))
|
|
|
+ {
|
|
|
+ setting.hwTime = now;
|
|
|
+ setting.hw = hw.GetString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(study, study.id, new PartitionKey($"{study.code}"));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Ok(new { code = HttpStatusCode.NotFound });
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(new { code = HttpStatusCode.OK});
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/upload()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("delete")]
|
|
|
+ public async Task<IActionResult> Delete(JsonElement request)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ var sresponse = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(id.ToString(), new PartitionKey($"Study-{code}"));
|
|
|
+ if (sresponse.Status == 200)
|
|
|
+ {
|
|
|
+ using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
|
|
|
+ Study study = json.ToObject<Study>();
|
|
|
+ if (!string.IsNullOrEmpty(study.detail.examId)) {
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").DeleteItemStreamAsync(study.detail.examId, new PartitionKey($"TrExam-{code}"));
|
|
|
+ }
|
|
|
+ if (!string.IsNullOrEmpty(study.detail.surveyId)) {
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").DeleteItemStreamAsync(study.detail.surveyId, new PartitionKey($"TrSurvey-{code}"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var response = await client.GetContainer("TEAMModelOS", "Common").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Study-{code}"));
|
|
|
+ return Ok(new { id, code = response.Status });
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/delete()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <param name="request"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("find")]
|
|
|
+ public async Task<IActionResult> Find(JsonElement requert)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ var query = $"select c.id,c.detail.img as img,c.name,c.type,c.detail.startTime,c.detail.endTime,c.detail.presenter,c.detail.topic,c.detail.address,c.owner from c ";
|
|
|
+ string continuationToken = string.Empty;
|
|
|
+ string token = default;
|
|
|
+ //是否需要进行分页查询,默认不分页
|
|
|
+ bool iscontinuation = false;
|
|
|
+ if (requert.TryGetProperty("token", out JsonElement token_1))
|
|
|
+ {
|
|
|
+ token = token_1.GetString();
|
|
|
+ iscontinuation = true;
|
|
|
+ };
|
|
|
+ //默认不指定返回大小
|
|
|
+ int? topcout = null;
|
|
|
+ if (requert.TryGetProperty("count", out JsonElement jcount))
|
|
|
+ {
|
|
|
+ if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
|
|
|
+ {
|
|
|
+ topcout = data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<object> studies = new();
|
|
|
+ await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query, continuationToken: token, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Study-{code}") }))
|
|
|
+ {
|
|
|
+
|
|
|
+ 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())
|
|
|
+ {
|
|
|
+ studies.Add(obj.ToObject<object>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (iscontinuation)
|
|
|
+ {
|
|
|
+ continuationToken = item.GetContinuationToken();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return Ok(new { studies });
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/find()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("find-summary")]
|
|
|
+ public async Task<IActionResult> FindSummary(JsonElement requert)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ List<Study> studies = new();
|
|
|
+ await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Study>(queryText: $"select value(c) from c where c.id = '{id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Study-{code}") }))
|
|
|
+ {
|
|
|
+ studies.Add(item);
|
|
|
+ }
|
|
|
+ return Ok(new { studies });
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/FindSummary()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <param name="request"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("find-by-teacher")]
|
|
|
+ public async Task<IActionResult> FindByTeacher(JsonElement requert)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ if (!requert.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ var query = $"select c.id,c.detail.img as img,c.name,c.detail.startTime,c.type,c.detail.endTime,c.detail.presenter,c.detail.topic,c.detail.address,c.owner from c join A0 in c.teachers where A0.id = '{tId}'";
|
|
|
+ List<object> studies = new();
|
|
|
+ await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Study-{code}") }))
|
|
|
+ {
|
|
|
+
|
|
|
+ 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())
|
|
|
+ {
|
|
|
+ studies.Add(obj.ToObject<object>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Ok(new { studies });
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/find-by-teacher()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("find-summary-by-teacher")]
|
|
|
+ public async Task<IActionResult> FindSummaryByTeacher(JsonElement requert)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ if (!requert.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ List<object> studies = new();
|
|
|
+ await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c join A0 in c.teachers where A0.id = '{tId}' and c.id = '{id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Study-{code}") }))
|
|
|
+ {
|
|
|
+ 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())
|
|
|
+ {
|
|
|
+ studies.Add(obj.ToObject<object>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Ok(new { studies });
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},study/find-summary-by-teacher()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ [ProducesDefaultResponseType]
|
|
|
+ //[AuthToken(Roles = "Teacher")]
|
|
|
+ [HttpPost("audit")]
|
|
|
+ public async Task<IActionResult> Audit(JsonElement request)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
|
|
|
+ if (!request.TryGetProperty("type", out JsonElement type)) return BadRequest();
|
|
|
+ var client = _azureCosmos.GetCosmosClient();
|
|
|
+ long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
|
+ var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(id.ToString(), new PartitionKey($"Study-{code}"));
|
|
|
+ if (response.Status == (int)HttpStatusCode.OK)
|
|
|
+ {
|
|
|
+ var json = await JsonDocument.ParseAsync(response.ContentStream);
|
|
|
+ Study study = json.ToObject<Study>();
|
|
|
+ bool flag = study.teachers.Exists(s => s.id.Equals(tId.GetString()));
|
|
|
+ if (flag)
|
|
|
+ {
|
|
|
+ foreach (Setting setting in study.teachers)
|
|
|
+ {
|
|
|
+ if (setting.id.Equals(tId.GetString()))
|
|
|
+ {
|
|
|
+ setting.status = type.GetInt32();
|
|
|
+ setting.aTime = now;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(study, study.id, new PartitionKey($"{study.code}"));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Ok(new { code = HttpStatusCode.NotFound });
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(new { code = HttpStatusCode.OK });
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ await _dingDing.SendBotMsg($"OS,{_option.Location},Study/audit()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
|
|
|
+ return BadRequest();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|