添加项目文件。

master
wenjy 10 months ago
parent ef182fbc3a
commit f4fc787d79

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq;
using System.Text;
namespace SlnMesnac.Common
{
/// <summary>
/// 谓词表达式构建器
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// 创建一个值恒为 <c>true</c> 的表达式。
/// </summary>
/// <typeparam name="T">表达式方法类型</typeparam>
/// <returns>一个值恒为 <c>true</c> 的表达式。</returns>
public static Expression<Func<T, bool>> True<T>() { return p => true; }
/// <summary>
/// 创建一个值恒为 <c>false</c> 的表达式。
/// </summary>
/// <typeparam name="T">表达式方法类型</typeparam>
/// <returns>一个值恒为 <c>false</c> 的表达式。</returns>
public static Expression<Func<T, bool>> False<T>() { return f => false; }
/// <summary>
/// 使用 Expression.OrElse 的方式拼接两个 System.Linq.Expression。
/// </summary>
/// <typeparam name="T">表达式方法类型</typeparam>
/// <param name="left">左边的 System.Linq.Expression 。</param>
/// <param name="right">右边的 System.Linq.Expression。</param>
/// <returns>拼接完成的 System.Linq.Expression。</returns>
public static Expression<T> Or<T>(this Expression<T> left, Expression<T> right)
{
return MakeBinary(left, right, Expression.OrElse);
}
/// <summary>
/// 使用 Expression.AndAlso 的方式拼接两个 System.Linq.Expression。
/// </summary>
/// <typeparam name="T">表达式方法类型</typeparam>
/// <param name="left">左边的 System.Linq.Expression 。</param>
/// <param name="right">右边的 System.Linq.Expression。</param>
/// <returns>拼接完成的 System.Linq.Expression。</returns>
public static Expression<T> And<T>(this Expression<T> left, Expression<T> right)
{
return MakeBinary(left, right, Expression.AndAlso);
}
/// <summary>
/// 使用自定义的方式拼接两个 System.Linq.Expression。
/// </summary>
/// <typeparam name="T">表达式方法类型</typeparam>
/// <param name="left">左边的 System.Linq.Expression 。</param>
/// <param name="right">右边的 System.Linq.Expression。</param>
/// <param name="func"> </param>
/// <returns>拼接完成的 System.Linq.Expression。</returns>
private static Expression<T> MakeBinary<T>(this Expression<T> left, Expression<T> right, Func<Expression, Expression, Expression> func)
{
return MakeBinary((LambdaExpression)left, right, func) as Expression<T>;
}
/// <summary>
/// 拼接两个 <paramref>
/// <name>System.Linq.Expression</name>
/// </paramref> ,两个 <paramref>
/// <name>System.Linq.Expression</name>
/// </paramref> 的参数必须完全相同。
/// </summary>
/// <param name="left">左边的 <paramref>
/// <name>System.Linq.Expression</name>
/// </paramref> </param>
/// <param name="right">右边的 <paramref>
/// <name>System.Linq.Expression</name>
/// </paramref> </param>
/// <param name="func">表达式拼接的具体逻辑</param>
/// <returns>拼接完成的 <paramref>
/// <name>System.Linq.Expression</name>
/// </paramref> </returns>
private static LambdaExpression MakeBinary(this LambdaExpression left, LambdaExpression right, Func<Expression, Expression, Expression> func)
{
var data = Combinate(right.Parameters, left.Parameters).ToArray();
right = ParameterReplace.Replace(right, data) as LambdaExpression;
return Expression.Lambda(func(left.Body, right.Body), left.Parameters.ToArray());
}
/// <summary>
/// 合并参数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns></returns>
private static IEnumerable<KeyValuePair<T, T>> Combinate<T>(IEnumerable<T> left, IEnumerable<T> right)
{
var a = left.GetEnumerator();
var b = right.GetEnumerator();
while (a.MoveNext() && b.MoveNext())
yield return new KeyValuePair<T, T>(a.Current, b.Current);
}
}
internal sealed class ParameterReplace : ExpressionVisitor
{
public static Expression Replace(Expression e, IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>> paramList)
{
var item = new ParameterReplace(paramList);
return item.Visit(e);
}
private readonly Dictionary<ParameterExpression, ParameterExpression> _parameters;
public ParameterReplace(IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>> paramList)
{
_parameters = paramList.ToDictionary(p => p.Key, p => p.Value, new ParameterEquality());
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression result;
if (_parameters.TryGetValue(p, out result))
return result;
return base.VisitParameter(p);
}
#region class: ParameterEquality
private class ParameterEquality : IEqualityComparer<ParameterExpression>
{
public bool Equals(ParameterExpression x, ParameterExpression y)
{
if (x == null || y == null)
return false;
return x.Type == y.Type;
}
public int GetHashCode(ParameterExpression obj)
{
if (obj == null)
return 0;
return obj.Type.GetHashCode();
}
}
#endregion
}
}

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -0,0 +1,28 @@
using Microsoft.Extensions.Options;
using System;
namespace SlnMesnac.Config
{
/// <summary>
/// 系统配置
/// </summary>
public class AppConfig : IOptions<AppConfig>
{
/// <summary>
/// 日志文件路径
/// </summary>
public string logPath { get; set; }
/// <summary>
/// MES 数据库连接字符串
/// </summary>
public string mesConnStr { get; set; }
/// <summary>
/// MCS 数据库连接字符串
/// </summary>
public string mcsConnStr { get; set; }
public AppConfig Value => this;
}
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.1" />
</ItemGroup>
</Project>

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.134" />
</ItemGroup>
</Project>

@ -0,0 +1,284 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Linq;
namespace SlnMesnac.Model.domain
{
[SugarTable("BasMaterial"), TenantAttribute("mes")]
[DataContract(Name = "BasMaterial 物料信息")]
public class BaseMaterialInfo
{
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ObjID", IsPrimaryKey = true)]
public int ObjID { get; set; }
/// <summary>
/// 大类+小类+规格+胶代码或最后4顺序号
///</summary>
[SugarColumn(ColumnName = "MaterialCode")]
public string MaterialCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MajorTypeID")]
public int? MajorTypeID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MinorTypeID")]
public string MinorTypeID { get; set; }
/// <summary>
///
/// 默认值: ('')
///</summary>
[SugarColumn(ColumnName = "RubCode")]
public string RubCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaterialName")]
public string MaterialName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaterialOtherName")]
public string MaterialOtherName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaterialSimpleName")]
public string MaterialSimpleName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ProductMaterialCode")]
public string ProductMaterialCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaterialLevel")]
public string MaterialLevel { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaterialGroup")]
public string MaterialGroup { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UserCode")]
public string UserCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "PlanPrice")]
public decimal? PlanPrice { get; set; }
/// <summary>
/// 具体到县级市长度为6国外的只具体国家
///</summary>
[SugarColumn(ColumnName = "ProductArea")]
public string ProductArea { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MinStock")]
public decimal? MinStock { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MaxStock")]
public decimal? MaxStock { get; set; }
/// <summary>
/// 千克,克,块,桶,升
///</summary>
[SugarColumn(ColumnName = "UnitID")]
public int? UnitID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "StaticUnitID")]
public int? StaticUnitID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "StaticUnitCoefficient")]
public decimal? StaticUnitCoefficient { get; set; }
/// <summary>
/// 显为百分比
///</summary>
[SugarColumn(ColumnName = "CheckPermitError")]
public decimal? CheckPermitError { get; set; }
/// <summary>
/// 按小时计算
///</summary>
[SugarColumn(ColumnName = "MaxParkTime")]
public decimal? MaxParkTime { get; set; }
/// <summary>
/// 小时计算
///</summary>
[SugarColumn(ColumnName = "MinParkTime")]
public decimal? MinParkTime { get; set; }
/// <summary>
///
/// 默认值: (getdate())
///</summary>
[SugarColumn(ColumnName = "DefineDate")]
public DateTime? DefineDate { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "StandardCode")]
public string StandardCode { get; set; }
/// <summary>
///
/// 默认值: ((1))
///</summary>
[SugarColumn(ColumnName = "StaticClass")]
public int? StaticClass { get; set; }
/// <summary>
///
/// 默认值: ((0))
///</summary>
[SugarColumn(ColumnName = "IsEqualMaterial")]
public string IsEqualMaterial { get; set; }
/// <summary>
///
/// 默认值: ((0))
///</summary>
[SugarColumn(ColumnName = "IsPutJar")]
public string IsPutJar { get; set; }
/// <summary>
///
/// 默认值: ((1))
///</summary>
[SugarColumn(ColumnName = "IsQualityRateCount")]
public string IsQualityRateCount { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ERPCode")]
public string ERPCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "Remark")]
public string Remark { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "DeleteFlag")]
public string DeleteFlag { get; set; }
/// <summary>
///
/// 默认值: ((0))
///</summary>
[SugarColumn(ColumnName = "ValidDate")]
public decimal ValidDate { get; set; }
/// <summary>
///
/// 默认值: ((0))
///</summary>
[SugarColumn(ColumnName = "ValidDateB")]
public decimal ValidDateB { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "SAPMaterialCode")]
public string SAPMaterialCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "SAPMaterialShortCode")]
public string SAPMaterialShortCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ERPCode_Bak")]
public string ErpcodeBak { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "OperSourceTemp")]
public string OperSourceTemp { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "IsQualityrateCountBak")]
public int? IsQualityrateCountBak { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "CMaterialLevel")]
public string CMaterialLevel { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "CMaterialGroup")]
public string CMaterialGroup { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "XBStock")]
public decimal? XBStock { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "HFCode")]
public string HFCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "HFCode2")]
public string HFCode2 { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UNITName")]
public string UNITName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "KFACSTATUS")]
public string Kfacstatus { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "Ordertype")]
public string Ordertype { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "CreateDate")]
public string CreateDate { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UpdateDate")]
public string UpdateDate { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UnAudit")]
public string UnAudit { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ChkStand")]
public string ChkStand { get; set; }
/// <summary>
/// 1 KG 2 PC 3 标准KG
/// 默认值: ((1))
///</summary>
[SugarColumn(ColumnName = "SapUpUnit")]
public int? SapUpUnit { get; set; }
/// <summary>
/// 是否为轨道事业部物料0不是1
///</summary>
[SugarColumn(ColumnName = "IsGDMaterial")]
public int? IsGDMaterial { get; set; }
}
}

@ -0,0 +1,108 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Principal;
using System.Text;
using System.Xml.Linq;
namespace SlnMesnac.Model.domain
{
/// <summary>
/// 系统基础资料-人员基础资料 yuany
///</summary>
[SugarTable("BasUser"), TenantAttribute("mes")]
[DataContract(Name = "BaseUser 人员基础资料")]
public class BaseUser
{
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ObjID", IsPrimaryKey = true, IsIdentity = true)]
public int ObjID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UserName")]
public string UserName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UserPWD")]
public string UserPWD { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "RealName")]
public string RealName { get; set; }
/// <summary>
///
/// 默认值: ((1))
///</summary>
[SugarColumn(ColumnName = "Sex")]
public int? Sex { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "Telephone")]
public string Telephone { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "WorkBarcode")]
public string WorkBarcode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "DeptCode")]
public string DeptCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "WorkID")]
public int? WorkID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ShiftID")]
public int? ShiftID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "WorkShopID")]
public int? WorkShopID { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "HRCode")]
public string HRCode { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ERPCode")]
public string ERPCode { get; set; }
/// <summary>
///
/// 默认值: ((0))
///</summary>
[SugarColumn(ColumnName = "DeleteFlag")]
public string DeleteFlag { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "Remark")]
public string Remark { get; set; }
/// <summary>
///
/// 默认值: ((1))
///</summary>
[SugarColumn(ColumnName = "IsEmployee")]
public string IsEmployee { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "RecordTime")]
public DateTime? RecordTime { get; set; }
}
}

@ -0,0 +1,25 @@
using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SlnMesnac.Quartz.Job
{
internal class Job2 : IJob
{
private readonly ILogger<Job2> _logger;
public Job2(ILogger<Job2> logger)
{
_logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogInformation($"执行Job2{DateTime.Now.ToString("HH:mm:ss")}");
return Task.CompletedTask;
}
}
}

@ -0,0 +1,25 @@
using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SlnMesnac.Quartz.Job
{
public class MyJob : IJob
{
private readonly ILogger<MyJob> _logger;
public MyJob(ILogger<MyJob> logger)
{
_logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogInformation($"执行MyJob{DateTime.Now.ToString("HH:mm:ss")}");
return Task.CompletedTask;
}
}
}

@ -0,0 +1,32 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using SlnMesnac.Quartz.Job;
using System;
namespace SlnMesnac.Quartz
{
public static class QuartzSetUp
{
[Obsolete]
public static void AddQuartzSetUp(this IServiceCollection services)
{
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.ScheduleJob<MyJob>(trigger =>
trigger.WithCronSchedule("*/3 * * * * ?").WithIdentity("MyJob", "MyJobGroup") // 示例每3s执行一次
);
q.ScheduleJob<Job2>(trigger =>
trigger.WithCronSchedule("*/5 * * * * ?").WithIdentity("Job2", "Job2Group") // 示例每5s执行一次
);
});
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
services.AddSingleton<IScheduler>(provider => provider.GetRequiredService<ISchedulerFactory>().GetScheduler().Result);
}
}
}

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
</ItemGroup>
</Project>

@ -0,0 +1,20 @@
using SqlSugar;
using System;
namespace SlnMesnac.Repository
{
public class Repository<T> : SimpleClient<T> where T : class, new()
{
public ITenant itenant = null;//多租户事务、GetConnection、IsAnyConnection等功能
public Repository(ISqlSugarClient db)
{
itenant = db.AsTenant();//用来处理事务
base.Context = db.AsTenant().GetConnectionScopeWithAttr<T>();//获取子Db
//如果不想通过注入多个仓储
//用到ChangeRepository或者Db.GetMyRepository需要看标题4写法
}
}
}

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SlnMesnac.Common\SlnMesnac.Common.csproj" />
<ProjectReference Include="..\SlnMesnac.Config\SlnMesnac.Config.csproj" />
<ProjectReference Include="..\SlnMesnac.Model\SlnMesnac.Model.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,63 @@
using Microsoft.Extensions.DependencyInjection;
using SlnMesnac.Config;
using SlnMesnac.Repository.service;
using SlnMesnac.Repository.service.Impl;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Text;
namespace SlnMesnac.Repository
{
public static class SqlsugarSetup
{
/// <summary>
/// 注册SqlSugar
/// </summary>
/// <param name="services"></param>
public static void AddSqlSugarSetup(this IServiceCollection services)
{
services.AddSingleton<ISqlSugarClient>(x =>
{
var appConfig = x.GetService<AppConfig>();
SqlSugarScope Db = new SqlSugarScope(new List<ConnectionConfig>()
{
new ConnectionConfig()
{
ConfigId = "mes",
DbType = DbType.SqlServer,
ConnectionString = appConfig.mesConnStr,
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true,
},
new ConnectionConfig()
{
ConfigId = "mcs",
DbType = DbType.Oracle,
ConnectionString = appConfig.mcsConnStr,
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true,
}
},
db =>
{
db.Aop.OnLogExecuting = (sql, pars) => { };
});
return Db;
});
}
/// <summary>
/// 注册Repository、Service服务
/// </summary>
/// <param name="services"></param>
public static void AddServices(this IServiceCollection services)
{
//services.AddSingleton<Repository<BaseUser>>();
services.AddSingleton(typeof(Repository<>));
services.AddSingleton<IBaseUserService, BaseUserServiceImpl>();
services.AddSingleton<IBaseMaterialService, BaseMaterialServiceImpl>();
}
}
}

@ -0,0 +1,39 @@
using SlnMesnac.Model.domain;
using System;
using System.Collections.Generic;
using System.Text;
namespace SlnMesnac.Repository.service
{
public interface IBaseMaterialService
{
/// <summary>
/// 获取所有的物料信息
/// </summary>
/// <returns></returns>
List<BaseMaterialInfo> GetMaterialInfos();
/// <summary>
/// 通过物料编码获取物料信息
/// </summary>
/// <param name="materialCode"></param>
/// <returns></returns>
BaseMaterialInfo GetMaterialInfoByMaterialCode(string materialCode);
/// <summary>
/// 通过SAP物料编码获取物料信息
/// </summary>
/// <param name="sapMaterialCode"></param>
/// <returns></returns>
BaseMaterialInfo GetMaterialInfoBySapMaterialCode(string sapMaterialCode);
/// <summary>
/// 通过物料类别获取物料信息
/// </summary>
/// <param name="majorTypeId">物料大类</param>
/// <param name="minorTypeId">物料细类</param>
/// <returns></returns>
List<BaseMaterialInfo> GetMaterialInfosByMaterialType(int majorTypeId, string minorTypeId);
}
}

@ -0,0 +1,23 @@
using SlnMesnac.Model.domain;
using System;
using System.Collections.Generic;
using System.Text;
namespace SlnMesnac.Repository.service
{
public interface IBaseUserService
{
/// <summary>
/// 获取用户信息
/// </summary>
/// <returns></returns>
List<BaseUser> GetUsers();
/// <summary>
/// 验证添加用户信息,有一个错误时进行回滚
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
bool InsertUsers(List<BaseUser> users);
}
}

@ -0,0 +1,111 @@
using Microsoft.Extensions.Logging;
using SlnMesnac.Model.domain;
using SlnMesnac.Common;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
namespace SlnMesnac.Repository.service.Impl
{
public class BaseMaterialServiceImpl : IBaseMaterialService
{
private Repository<BaseMaterialInfo> _repository;
private ILogger<BaseMaterialServiceImpl> _logger;
public BaseMaterialServiceImpl(Repository<BaseMaterialInfo> repository, ILogger<BaseMaterialServiceImpl> logger)
{
_repository = repository;
_logger = logger;
}
/// <summary>
/// 通过物料编码获取物料信息
/// </summary>
/// <param name="materialCode"></param>
/// <returns></returns>
public BaseMaterialInfo GetMaterialInfoByMaterialCode(string materialCode)
{
BaseMaterialInfo materialInfo = null;
try
{
materialInfo = _repository.GetFirst(x => x.MaterialCode == materialCode);
}
catch (Exception ex)
{
_logger.LogError($"根据物料编号获取物料信息异常:{ex.Message}");
}
return materialInfo;
}
/// <summary>
/// 通过SAP物料编码获取物料信息
/// </summary>
/// <param name="sapMaterialCode"></param>
/// <returns></returns>
public BaseMaterialInfo GetMaterialInfoBySapMaterialCode(string sapMaterialCode)
{
BaseMaterialInfo materialInfo = null;
try
{
materialInfo = _repository.GetFirst(x => x.SAPMaterialCode == sapMaterialCode);
}
catch (Exception ex)
{
_logger.LogError($"根据SAP物料编号获取物料信息异常:{ex.Message}");
}
return materialInfo;
}
/// <summary>
/// 获取所有的物料信息
/// </summary>
/// <returns></returns>
public List<BaseMaterialInfo> GetMaterialInfos()
{
List<BaseMaterialInfo> materialInfos = null;
try
{
materialInfos = _repository.GetList();
}
catch (Exception ex)
{
_logger.LogError($"获取物料信息异常:{ex.Message}");
}
return materialInfos;
}
/// <summary>
/// 通过物料类别获取物料信息
/// </summary>
/// <param name="majorTypeId">物料大类</param>
/// <param name="minorTypeId">物料细类</param>
/// <returns></returns>
public List<BaseMaterialInfo> GetMaterialInfosByMaterialType(int majorTypeId, string minorTypeId)
{
List<BaseMaterialInfo> materialInfos = null;
try
{
Expression<Func<BaseMaterialInfo, bool>> exp = x => true;
if (majorTypeId != 0)
{
exp = exp.And(x => x.MajorTypeID == majorTypeId);
}
else if (!string.IsNullOrEmpty(minorTypeId))
{
exp = exp.And(x => x.MinorTypeID == minorTypeId);
}
materialInfos = _repository.GetList(exp);
}
catch (Exception ex)
{
_logger.LogError($"通过物料类型获取物料信息异常:{ex.Message}");
}
return materialInfos;
}
}
}

@ -0,0 +1,55 @@
using Microsoft.Extensions.Logging;
using SlnMesnac.Model.domain;
using System;
using System.Collections.Generic;
using System.Text;
namespace SlnMesnac.Repository.service.Impl
{
public class BaseUserServiceImpl : IBaseUserService
{
private readonly ILogger<BaseUser> _logger;
private readonly Repository<BaseUser> _rep;
public BaseUserServiceImpl(ILogger<BaseUser> logger, Repository<BaseUser> rep)
{
_logger = logger;
_rep = rep;
}
public List<BaseUser> GetUsers()
{
List<BaseUser> users = null;
try
{
users = _rep.GetList();
}
catch (Exception ex)
{
_logger.LogError($"获取用户信息异常{ex.Message}");
}
return users;
}
public bool InsertUsers(List<BaseUser> users)
{
bool result = false;
try
{
_rep.AsTenant().BeginTran();
result = _rep.InsertRange(users);
_rep.AsTenant().CommitTran();
}
catch (Exception ex)
{
_rep.AsTenant().RollbackTran();
_logger.LogError($"用户信息添加异常:{ex.Message}");
}
return result;
}
}
}

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;
using SlnMesnac.Config;
using System;
using System.IO;
namespace SlnMesnac.Serilog
{
/// <summary>
/// Serilog
/// </summary>
public static class SerilogExtensions
{
public static IApplicationBuilder UseSerilogExtensions(this IApplicationBuilder app)
{
//启用Serilog中间件
app.UseSerilogRequestLogging();
#region 通过配置文件读取日志存放位置
var appConfig = app.ApplicationServices.GetService<AppConfig>();
var logPath = $"{appConfig.logPath}/Logs/{DateTime.UtcNow:yyyy-MM-dd}/";
#endregion
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console()
.WriteTo.File(Path.Combine(logPath, "Info.log"), LogEventLevel.Information, fileSizeLimitBytes: 5 * 1024)
.WriteTo.File(Path.Combine(logPath, "Error.log"), LogEventLevel.Error, fileSizeLimitBytes: 5 * 1024)
.WriteTo.File(Path.Combine(logPath, "Warn.log"), LogEventLevel.Warning, fileSizeLimitBytes: 5 * 1024)
//.WriteTo.File(Path.Combine(logPath, "Debug.log"), LogEventLevel.Debug, fileSizeLimitBytes: 5 * 1024)
.CreateLogger();
app.UseSerilogRequestLogging();
Log.Information($"项目初始化完成,日志存放路径:{appConfig.logPath}");
return app;
}
}
}

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SlnMesnac.Config\SlnMesnac.Config.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,61 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33502.453
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlnMesnac", "SlnMesnac\SlnMesnac.csproj", "{19445879-B3F3-4C76-A670-EFAE9E25BAB4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlnMesnac.Common", "SlnMesnac.Common\SlnMesnac.Common.csproj", "{BB71F26A-7007-423E-83E9-7A3BAC25E934}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlnMesnac.Config", "SlnMesnac.Config\SlnMesnac.Config.csproj", "{6EF7F087-7149-4689-885C-E0D05E1A9AA8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlnMesnac.Model", "SlnMesnac.Model\SlnMesnac.Model.csproj", "{9EC081B6-971F-418C-A40C-5B8AD2E27417}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlnMesnac.Repository", "SlnMesnac.Repository\SlnMesnac.Repository.csproj", "{C892C06A-496B-43B6-AEC7-AF9D70778C0C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlnMesnac.Quartz", "SlnMesnac.Quartz\SlnMesnac.Quartz.csproj", "{12ED397C-951E-411C-9C43-CDABA79CA45B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlnMesnac.Serilog", "SlnMesnac.Serilog\SlnMesnac.Serilog.csproj", "{DEE2F305-733C-47C8-891C-502121ABAD00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{19445879-B3F3-4C76-A670-EFAE9E25BAB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19445879-B3F3-4C76-A670-EFAE9E25BAB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19445879-B3F3-4C76-A670-EFAE9E25BAB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19445879-B3F3-4C76-A670-EFAE9E25BAB4}.Release|Any CPU.Build.0 = Release|Any CPU
{BB71F26A-7007-423E-83E9-7A3BAC25E934}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB71F26A-7007-423E-83E9-7A3BAC25E934}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB71F26A-7007-423E-83E9-7A3BAC25E934}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB71F26A-7007-423E-83E9-7A3BAC25E934}.Release|Any CPU.Build.0 = Release|Any CPU
{6EF7F087-7149-4689-885C-E0D05E1A9AA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EF7F087-7149-4689-885C-E0D05E1A9AA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EF7F087-7149-4689-885C-E0D05E1A9AA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EF7F087-7149-4689-885C-E0D05E1A9AA8}.Release|Any CPU.Build.0 = Release|Any CPU
{9EC081B6-971F-418C-A40C-5B8AD2E27417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EC081B6-971F-418C-A40C-5B8AD2E27417}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EC081B6-971F-418C-A40C-5B8AD2E27417}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EC081B6-971F-418C-A40C-5B8AD2E27417}.Release|Any CPU.Build.0 = Release|Any CPU
{C892C06A-496B-43B6-AEC7-AF9D70778C0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C892C06A-496B-43B6-AEC7-AF9D70778C0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C892C06A-496B-43B6-AEC7-AF9D70778C0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C892C06A-496B-43B6-AEC7-AF9D70778C0C}.Release|Any CPU.Build.0 = Release|Any CPU
{12ED397C-951E-411C-9C43-CDABA79CA45B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12ED397C-951E-411C-9C43-CDABA79CA45B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12ED397C-951E-411C-9C43-CDABA79CA45B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12ED397C-951E-411C-9C43-CDABA79CA45B}.Release|Any CPU.Build.0 = Release|Any CPU
{DEE2F305-733C-47C8-891C-502121ABAD00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DEE2F305-733C-47C8-891C-502121ABAD00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DEE2F305-733C-47C8-891C-502121ABAD00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DEE2F305-733C-47C8-891C-502121ABAD00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D414D640-C2DE-44E2-930E-DD095881CDC1}
EndGlobalSection
EndGlobal

@ -0,0 +1,90 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SlnMesnac.Model.domain;
using SlnMesnac.Repository.service;
namespace SlnMesnac.Controllers
{
/// <summary>
/// 物料信息
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class BaseMaterialInfoController
{
private ILogger<BaseMaterialInfoController> _logger;
private IBaseMaterialService _service;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="service"></param>
public BaseMaterialInfoController(ILogger<BaseMaterialInfoController> logger, IBaseMaterialService service)
{
_logger = logger;
_service = service;
}
/// <summary>
/// 获取物料信息
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<BaseMaterialInfo> Get()
{
IEnumerable<BaseMaterialInfo> materialInfos = null;
try
{
materialInfos = _service.GetMaterialInfos();
}
catch (Exception ex)
{
_logger.LogError($"获取物料信息接口调用异常:{ex.Message}");
}
return materialInfos;
}
/// <summary>
/// 根据物料编号获取物料信息
/// </summary>
/// <param name="materialCode">物料编号</param>
/// <returns></returns>
[HttpGet("Get/{materialCode}")]
public BaseMaterialInfo GetMaterialInfoByMaterialCode(string materialCode)
{
BaseMaterialInfo materialInfo = null;
try
{
materialInfo = _service.GetMaterialInfoByMaterialCode(materialCode);
}
catch (Exception ex)
{
_logger.LogError($"根据物料编号获取物料信息接口调用异常:{ex.Message}");
}
return materialInfo;
}
/// <summary>
/// 通过物料类别获取物料信息
/// </summary>
/// <param name="majorTypeId">物料大类</param>
/// <param name="minorTypeId">物料细类</param>
/// <returns></returns>
[HttpGet("Get/{majorTypeId}/{minorTypeId}")]
public IEnumerable<BaseMaterialInfo> GetMaterialInfosByMaterialType(int majorTypeId, string minorTypeId)
{
IEnumerable<BaseMaterialInfo> materialInfos = null;
try
{
materialInfos = _service.GetMaterialInfosByMaterialType(majorTypeId, minorTypeId);
}
catch (Exception ex)
{
_logger.LogError($"通过物料类别获取物料信息接口调用异常:{ex.Message}");
}
return materialInfos;
}
}
}

@ -0,0 +1,80 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SlnMesnac.Model.domain;
using SlnMesnac.Repository.service;
namespace SlnMesnac.Controllers
{
/// <summary>
/// 人员基础信息
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class BaseUserController : ControllerBase
{
private readonly ILogger<BaseUserController> _logger;
private readonly IBaseUserService _service;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="service"></param>
public BaseUserController(ILogger<BaseUserController> logger, IBaseUserService service)
{
_logger = logger;
_service = service;
}
/// <summary>
/// 获取人员基础信息
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<BaseUser> Get()
{
IEnumerable<BaseUser> users = null;
try
{
users = _service.GetUsers();
}
catch (Exception ex)
{
_logger.LogError($"获取用户信息接口调用异常:{ex.Message}");
}
return users;
}
/// <summary>
/// 通过用户名称获取指定用户信息
/// </summary>
/// <param name="userName">用户名称</param>
/// <returns></returns>
[HttpGet("Gets/{userName}")]
public IEnumerable<BaseUser> GetUserByUserName(string userName)
{
IEnumerable<BaseUser> users = null;
try
{
users = _service.GetUsers();
}
catch (Exception ex)
{
_logger.LogError($"获取用户信息接口调用异常:{ex.Message}");
}
return users;
}
/// <summary>
/// 添加用户信息
/// </summary>
/// <param name="users">用户列表</param>
/// <returns></returns>
[HttpPut]
public bool InsertUserInfo(List<BaseUser> users)
{
return _service.InsertUsers(users);
}
}
}

@ -0,0 +1,61 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Quartz;
namespace SlnMesnac.Controllers
{
/// <summary>
/// 任务调度
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class JobController
{
private readonly IScheduler _scheduler;
/// <summary>
///
/// </summary>
/// <param name="scheduler"></param>
public JobController(IScheduler scheduler)
{
_scheduler = scheduler;
}
/// <summary>
/// 启动任务
/// </summary>
/// <param name="jobName">任务名称</param>
/// <param name="groupName">任务分组</param>
/// <returns></returns>
[HttpPost("start")]
public async Task<string> StartJob(string jobName, string groupName)
{
// 检查调度器是否已经启动
if (!_scheduler.IsStarted)
{
await _scheduler.Start();
}
var myJobKey = new JobKey(jobName, groupName);
await _scheduler.ResumeJob(myJobKey);
return "Job started successfully.";
}
/// <summary>
/// 停止任务
/// </summary>
/// <param name="jobName">任务名称</param>
/// <param name="groupName">任务分组</param>
/// <returns></returns>
[HttpPost("stop")]
public async Task<string> StopJob(string jobName, string groupName)
{
var myJobKey = new JobKey(jobName, groupName);
await _scheduler.PauseJob(myJobKey);
return "Job stopped successfully.";
}
}
}

@ -0,0 +1,32 @@
using Serilog;
namespace SlnMesnac
{
/// <summary>
/// Æô¶¯Àà
/// </summary>
public class Program
{
/// <summary>
/// Main·½·¨
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
/// <summary>
/// CreateHostBuilder
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:32147",
"sslPort": 44302
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
//"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SlnMesnac": {
"commandName": "Project",
"launchBrowser": true,
//"launchUrl": "swagger",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SlnMesnac.Config\SlnMesnac.Config.csproj" />
<ProjectReference Include="..\SlnMesnac.Model\SlnMesnac.Model.csproj" />
<ProjectReference Include="..\SlnMesnac.Quartz\SlnMesnac.Quartz.csproj" />
<ProjectReference Include="..\SlnMesnac.Repository\SlnMesnac.Repository.csproj" />
<ProjectReference Include="..\SlnMesnac.Serilog\SlnMesnac.Serilog.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,121 @@
using Microsoft.OpenApi.Models;
using Serilog;
using Serilog.Events;
using SlnMesnac.Config;
using SlnMesnac.Quartz;
using SlnMesnac.Repository;
using SlnMesnac.Serilog;
using System.Runtime.Serialization;
namespace SlnMesnac
{
/// <summary>
///
/// </summary>
public class Startup
{
/// <summary>
///
/// </summary>
/// <param name="configuration"></param>
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
/// <summary>
///
/// </summary>
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services"></param>
[Obsolete]
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//配置Swagger
services.AddSwaggerGen(swagger =>
{
//自定义接口信息
swagger.SwaggerDoc("V1.0", new OpenApiInfo
{
Title = "MES Web Api",
Version = "V1.0",
Description = $"API版本V1.0",
Contact = new OpenApiContact
{
Name = "MES Web Api",
Email = "wenjy@mesnac.com"
}
});
//自定义实体别名
swagger.CustomSchemaIds(type => type.GetCustomAttributes(typeof(DataContractAttribute), true)
.Cast<DataContractAttribute>()
.FirstOrDefault()?.Name);
//配置Action名称
var path = Path.Combine(AppContext.BaseDirectory, "SlnMesnac.xml");
swagger.IncludeXmlComments(path, true); // true : 显示控制器层注释
swagger.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序如果有多个就可以看见效果了。
});
//注册配置类
services.AddSingleton<AppConfig>(provider =>
{
var configuration = provider.GetService<IConfiguration>();
return configuration.GetSection("AppConfig").Get<AppConfig>();
});
//注册SqlSugar
services.AddSqlSugarSetup();
//注册服务
services.AddServices();
//注册任务调度
services.AddQuartzSetUp();
}
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//启用Swagger中间件
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/V1.0/swagger.json", "MES Web Api V1.0");
c.RoutePrefix = "";
});
//启用Serilog中间件
app.UseSerilogExtensions();
//app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

@ -0,0 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AppConfig": {
"logPath": "E:\\桌面\\日常代码\\SlnMesnac\\SlnMesnac\\bin\\Debug\\net6.0",
"mesConnStr": "server=.;uid=sa;pwd=123456;database=JiangYinMENS",
"mcsConnStr": "Data Source=175.27.215.92/helowin;User ID=aucma_scada;Password=aucma"
}
}
Loading…
Cancel
Save