|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Ys.Core.Common;
|
|
|
|
|
using djy.Paas.Model;
|
|
|
|
|
using djy.Paas.IService;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using FreeSql;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using AutoMapper;
|
|
|
|
|
using DotNetCore.CAP;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
namespace djy.Paas.Service
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 服务基类
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ServBase : DbContext, ICapSubscribe
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取登录者账户详情信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="UserId"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public djy.Model.User GetUserInfo(string UserId)
|
|
|
|
|
{
|
|
|
|
|
if (UserId.IsNull())
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return DbBus.Get(DbList.djyolddb).Select<djy.Model.User>().Where(w => w.GID == UserId.ToString()).ToOne();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建日志
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Message">消息</param>
|
|
|
|
|
/// <param name="ObjName">触发数据对象或分组</param>
|
|
|
|
|
/// <param name="DataJson">触发数据</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool _LogsAdd(string Message, string ObjName, object DataJson = null)
|
|
|
|
|
{
|
|
|
|
|
return _LogsAdd(Message, ObjName, DataJson, null, null);
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建日志
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Message">消息</param>
|
|
|
|
|
/// <param name="ObjName">触发数据对象或分组</param>
|
|
|
|
|
/// <param name="DataJson">触发数据</param>
|
|
|
|
|
/// <param name="ObjGid">触发数据guid</param>
|
|
|
|
|
/// <param name="ResultJson">返回的数据</param>
|
|
|
|
|
/// <param name="JsonNotWebOption">true 属性原型 false 小驼峰模式</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool _LogsAdd(string Message, string ObjName = null, object DataJson = null, string ObjGid = null, object ResultJson = null, bool JsonNotWebOption = true)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (DataJson == null)
|
|
|
|
|
{ DataJson = ""; }
|
|
|
|
|
if (ResultJson == null) {
|
|
|
|
|
ResultJson = "";
|
|
|
|
|
}
|
|
|
|
|
var _datajson = "";
|
|
|
|
|
var _resulitjson = "";
|
|
|
|
|
if (JsonNotWebOption)
|
|
|
|
|
{
|
|
|
|
|
_datajson = DataJson.GetType() == typeof(string) ? DataJson.ToString() : JsonSerializer.Serialize(DataJson);
|
|
|
|
|
_resulitjson = ResultJson.GetType() == typeof(string) ? ResultJson.ToString() : JsonSerializer.Serialize(ResultJson);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var _option = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
|
|
|
|
_datajson = DataJson.GetType() == typeof(string) ? DataJson.ToString() : JsonSerializer.Serialize(DataJson, _option);
|
|
|
|
|
_resulitjson = ResultJson.GetType() == typeof(string) ? ResultJson.ToString() : JsonSerializer.Serialize(ResultJson, _option);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return _LogsAdd(new tb_sys_Logs
|
|
|
|
|
{
|
|
|
|
|
Message = Message,
|
|
|
|
|
ObjName = ObjName,
|
|
|
|
|
ObjGid = ObjGid,
|
|
|
|
|
DataJson = _datajson,
|
|
|
|
|
ResultJson = _resulitjson
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch { return false; }
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建日志
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Logs"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool _LogsAdd(tb_sys_Logs Logs)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return _LogsAdd(new List<tb_sys_Logs>() { Logs });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建日志
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="DtoList"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool _LogsAdd(List<tb_sys_Logs> LogsList)
|
|
|
|
|
{
|
|
|
|
|
var count = 0;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
LogsList.ForEach(x=> { x.Init();x.SysCode = "djypaas"; });
|
|
|
|
|
count = DbBus.Get(DbList.Logsdb).Insert(LogsList).ExecuteAffrows();
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
|
|
|
|
|
return count > 0 ? true : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 服务基类
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">表名</typeparam>
|
|
|
|
|
/// <typeparam name="D">Dto 名称</typeparam>
|
|
|
|
|
public class ServBase<T,D>:ServBase where T:class,new()
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
///设置 数据库连接池KeyName
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="DbconnName">数据库连接池KeyName</param>
|
|
|
|
|
public ServBase(string DbconnName)
|
|
|
|
|
{
|
|
|
|
|
DbKeyName = DbconnName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 数据库链接池KeyName
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string DbKeyName { get; set; }
|
|
|
|
|
public virtual ReturnResult<object> Add(T Dto)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnResult<object>();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (Dto == null)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("数据对象不能为空!");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
if (typeof(T).GetMethod("Init") != null)
|
|
|
|
|
{
|
|
|
|
|
typeof(T).GetMethod("Init").Invoke(Dto, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Insert<T>(Dto).ExecuteAffrows();
|
|
|
|
|
rs.OK("创建成功!");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Gor: return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// gid批量删除
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="gidlist"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public virtual ReturnResult<object> Del(Guid?[] gidlist) {
|
|
|
|
|
var rs = new ReturnResult<object>();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof(T).GetField("Status") != null)
|
|
|
|
|
{
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Update<T>().Set(w => (w as DataBase).Status, -1).Where(w => gidlist.ToList().Contains((w as DataBase).Gid)).ExecuteAffrows();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Delete<T>().Where(w => gidlist.Contains((w as DataBase).Gid)).ExecuteAffrows();
|
|
|
|
|
}
|
|
|
|
|
rs.OK((int)rs.Data == 0 ? "操作成功但没有指定的数据对象!" : "删除成功!");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 条件删除
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Where"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
|
|
|
|
public virtual ReturnResult<object> Del(Expression<Func<T, bool>> Where)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnResult<object>();
|
|
|
|
|
if (Where != null)
|
|
|
|
|
{//条件删除
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Delete<T>().Where(Where).ExecuteAffrows();
|
|
|
|
|
rs.OK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据Dto条件查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Dto"></param>
|
|
|
|
|
/// <param name="Page"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public virtual ReturnPagedResult<T> GetList(T Dto, ApiFromDto Page)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnPagedResult<T>();
|
|
|
|
|
|
|
|
|
|
rs.Not("未实现");
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 条件查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Where"></param>
|
|
|
|
|
/// <param name="Page"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
|
|
|
|
public virtual ReturnPagedResult<D> GetList(Expression<Func<T, bool>> Where,ApiFromDto Page)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnPagedResult<D>();
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Select<T>().Where(Where).Count(out var TotalCount).Page(Page.Page,Page.Limit).ToList<D>();
|
|
|
|
|
rs.Pageset(Page,TotalCount);
|
|
|
|
|
rs.OK();
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual ReturnResult<T> GetId(long Id)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnResult<T>();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (Id < 0)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("错误的请求!");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Select<T>(Id).ToOne();
|
|
|
|
|
if (rs.Data == null)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("没有找到此对象");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
rs.OK("OK");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
Gor: return rs;
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据Gid获取
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Id"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public ReturnResult<T> GetId(Guid? Id)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnResult<T>();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Select<T>().Where("Gid=@gid", new { gid = Id }).ToOne();
|
|
|
|
|
if (rs.Data == null)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("没有找到此对象");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
rs.OK("OK");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
Gor: return rs;
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 条件获取
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Where"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public ReturnResult<T> GetId(Expression<Func<T, bool>> Where)
|
|
|
|
|
{
|
|
|
|
|
var rs=new ReturnResult<T>();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rs.Data= DbBus.Get(DbKeyName).Select<T>().Where(Where).ToOne();
|
|
|
|
|
if (rs.Data == null)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("没有找到此对象");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
rs.OK("OK");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 对数据对象更新
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="Dto">要更新的目标对象</param>
|
|
|
|
|
/// <param name="UpDto">更新的值</param>
|
|
|
|
|
/// <param name="UpColumn">指定更新字段</param>
|
|
|
|
|
/// <param name="Notcolumn">指定的跳过过滤字段</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public T _UpDataDto(ref T Dto,T UpDto, List<string> UpColumn = null, List<string> Notcolumn = null) {
|
|
|
|
|
|
|
|
|
|
var _notcolumn= new List<string> { "Id", "AddTime", "Gid", "Status" };
|
|
|
|
|
if (Notcolumn == null || Notcolumn.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
Notcolumn = _notcolumn;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Notcolumn.AddRange(_notcolumn);
|
|
|
|
|
}
|
|
|
|
|
if (UpColumn == null || UpColumn.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
UpColumn = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach (var item in typeof(T).GetProperties())
|
|
|
|
|
{
|
|
|
|
|
UpColumn.Add(item.Name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < UpColumn.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
UpColumn[i] = UpColumn[i].ToLower();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < Notcolumn.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
Notcolumn[i] = Notcolumn[i].ToLower();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//进行反射循环对比是否需要修改新的值后更新
|
|
|
|
|
var collist= new List<PropertyInfo>();
|
|
|
|
|
// collist = typeof(T).GetProperties().Where(w => UpColumn.Contains(w.Name) && !Notcolumn.Contains(w.Name)).ToList();
|
|
|
|
|
var typeclist = typeof(T).GetProperties().ToList();
|
|
|
|
|
foreach (var col in typeclist)
|
|
|
|
|
{
|
|
|
|
|
if (UpColumn.IndexOf(col.Name.ToLower()) >= 0 && Notcolumn.IndexOf(col.Name.ToLower()) < 0)
|
|
|
|
|
{
|
|
|
|
|
collist.Add(col);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var initData = new T();
|
|
|
|
|
var ilist = initData.GetType().GetMembers();
|
|
|
|
|
if (initData.GetType().GetMethod("Init") != null)
|
|
|
|
|
{
|
|
|
|
|
initData.GetType().GetMethod("Init").Invoke(initData, new object[] { });
|
|
|
|
|
}
|
|
|
|
|
foreach (var item in collist)
|
|
|
|
|
{//空值和等于创建初始化的将被忽律
|
|
|
|
|
|
|
|
|
|
var valuedata = item.GetValue(Dto);
|
|
|
|
|
var valueup = item.GetValue(UpDto);
|
|
|
|
|
var init = item.GetValue(initData);
|
|
|
|
|
//针对status特特殊处理
|
|
|
|
|
if (item.Name.ToLower() == "status")
|
|
|
|
|
{ init = 0; }
|
|
|
|
|
|
|
|
|
|
if (valueup != null && (!valueup.Equals(valuedata) && (!valueup.Equals(init))))
|
|
|
|
|
{
|
|
|
|
|
if (valueup.ToString().IsNotNull())
|
|
|
|
|
{
|
|
|
|
|
if (valueup.ToString() == "[null]")
|
|
|
|
|
item.SetValue(Dto,null);
|
|
|
|
|
else
|
|
|
|
|
item.SetValue(Dto, valueup);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Dto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 更新 更新规则 自动对比更新数据对 只对修改的进行修改 自动忽略 空值 初始化值
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Dto"></param>
|
|
|
|
|
/// <param name="Where">原始数据条件</param>
|
|
|
|
|
/// <param name="Notcolumn">不做修改的列名</param>
|
|
|
|
|
///<param name="UpColumn">指定要修改的列</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public virtual ReturnResult<object> Up(T Dto, Expression<Func<T, bool>> Where = null, List<string> UpColumn = null, List<string> Notcolumn = null)
|
|
|
|
|
{
|
|
|
|
|
var rs = new ReturnResult<object>();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (Notcolumn == null || Notcolumn.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
Notcolumn = new List<string> { "Id", "AddTime" };
|
|
|
|
|
}
|
|
|
|
|
if (UpColumn == null || UpColumn.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
UpColumn = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach (var item in typeof(T).GetProperties())
|
|
|
|
|
{
|
|
|
|
|
UpColumn.Add(item.Name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (Dto == null)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("请求的数据不能空!");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
var GetRs = new ReturnResult<T>();
|
|
|
|
|
if (Where == null)
|
|
|
|
|
{
|
|
|
|
|
GetRs = this.GetId((long)Dto.GetType().GetProperty("Id").GetValue(Dto));
|
|
|
|
|
if (!GetRs.Status)
|
|
|
|
|
{
|
|
|
|
|
var gid = Dto.GetType().GetProperty("Gid").GetValue(Dto);
|
|
|
|
|
if (gid != null)
|
|
|
|
|
{
|
|
|
|
|
GetRs = this.GetId((Guid?)gid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GetRs = this.GetId(Where);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!GetRs.Status)
|
|
|
|
|
{
|
|
|
|
|
rs.Not("要修改数据对象不存在!");
|
|
|
|
|
goto Gor;
|
|
|
|
|
}
|
|
|
|
|
var Updata = GetRs.Data;
|
|
|
|
|
|
|
|
|
|
_UpDataDto(ref Updata,Dto,UpColumn,Notcolumn);
|
|
|
|
|
|
|
|
|
|
rs.Data = DbBus.Get(DbKeyName).Update<T>().SetSource(Updata).ExecuteAffrows();
|
|
|
|
|
rs.OK("更新成功!");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
rs.Not(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
Gor: return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|