103 changed files with 1671 additions and 285 deletions
@ -0,0 +1,43 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio Version 17 |
||||
|
VisualStudioVersion = 17.7.34024.191 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SiNan.API", "SiNan.API\SiNan.API.csproj", "{33F844AA-8786-4294-8F5A-2E5ED03D56FF}" |
||||
|
EndProject |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SiNan.Business", "SiNan.Business\SiNan.Business.csproj", "{D936217A-5B0C-4E88-B9BD-F97A3C2EA1D9}" |
||||
|
EndProject |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SiNan.Model", "SiNan.Model\SiNan.Model.csproj", "{8524A12A-FE6B-49BC-866B-52CB53C8BD1F}" |
||||
|
EndProject |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SiNan.Common", "SiNan.Common\SiNan.Common.csproj", "{521CB52C-EA2F-482D-AE07-EEF966552094}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{D936217A-5B0C-4E88-B9BD-F97A3C2EA1D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{D936217A-5B0C-4E88-B9BD-F97A3C2EA1D9}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{D936217A-5B0C-4E88-B9BD-F97A3C2EA1D9}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{D936217A-5B0C-4E88-B9BD-F97A3C2EA1D9}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{8524A12A-FE6B-49BC-866B-52CB53C8BD1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{8524A12A-FE6B-49BC-866B-52CB53C8BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{8524A12A-FE6B-49BC-866B-52CB53C8BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{8524A12A-FE6B-49BC-866B-52CB53C8BD1F}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{521CB52C-EA2F-482D-AE07-EEF966552094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{521CB52C-EA2F-482D-AE07-EEF966552094}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{521CB52C-EA2F-482D-AE07-EEF966552094}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{521CB52C-EA2F-482D-AE07-EEF966552094}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ExtensibilityGlobals) = postSolution |
||||
|
SolutionGuid = {A3B189CF-4897-44E3-A2E2-BB445F7512FD} |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
@ -0,0 +1,30 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using SiNan.Common.Models; |
||||
|
|
||||
|
namespace SiNan.API.Filters |
||||
|
{ |
||||
|
public class ResultFilter : IResultFilter |
||||
|
{ |
||||
|
public void OnResultExecuted(ResultExecutedContext context) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void OnResultExecuting(ResultExecutingContext context) |
||||
|
{ |
||||
|
if (context.Result is ObjectResult) |
||||
|
{ |
||||
|
var objectResult = context.Result as ObjectResult; |
||||
|
if (!(objectResult.Value is ApiResponse)) |
||||
|
{ |
||||
|
objectResult.Value = new ApiResponse() { Data = objectResult.Value }; |
||||
|
} |
||||
|
} |
||||
|
else if (context.Result is EmptyResult) |
||||
|
{ |
||||
|
context.Result = new ObjectResult(new ApiResponse()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using Microsoft.Extensions.Primitives; |
||||
|
using SiNan.Common.Models; |
||||
|
|
||||
|
namespace SiNan.API.Middlewares |
||||
|
{ |
||||
|
public class ClientVersionValidationMiddleWare |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 管道请求委托
|
||||
|
/// </summary>
|
||||
|
private RequestDelegate _next; |
||||
|
|
||||
|
private IDictionary<string, int> apiVersionDictionary; |
||||
|
|
||||
|
private IOptionsMonitor<List<ClientVersionValidationModel>> _monitor; |
||||
|
|
||||
|
public ClientVersionValidationMiddleWare(RequestDelegate requestDelegate, IOptionsMonitor<List<ClientVersionValidationModel>> monitor) |
||||
|
{ |
||||
|
_next = requestDelegate; |
||||
|
_monitor = monitor; |
||||
|
apiVersionDictionary = new Dictionary<string, int>(); |
||||
|
} |
||||
|
|
||||
|
public async Task Invoke(HttpContext context) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Console.WriteLine(context.Request.Path); |
||||
|
var apiRequirement = _monitor.CurrentValue.FirstOrDefault(x => x.Api.Equals(context.Request.Path, StringComparison.CurrentCultureIgnoreCase)); |
||||
|
if (apiRequirement != null) |
||||
|
{ |
||||
|
if (!context.Request.Headers.TryGetValue("ClientVersion", out StringValues clientVersionStr)) |
||||
|
throw new BusinessException("缺少版本信息,请更新司南"); |
||||
|
if (!int.TryParse(clientVersionStr, out int clientVersion)) |
||||
|
throw new BusinessException("版本信息不正确,请更新司南"); |
||||
|
if (clientVersion < apiRequirement.MinimumVersion) |
||||
|
throw new BusinessException("当前请求需更新司南"); |
||||
|
} |
||||
|
await _next(context); //调用管道执行下一个中间件
|
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ClientVersionValidationModel |
||||
|
{ |
||||
|
public string Api { get; set; } |
||||
|
|
||||
|
public int MinimumVersion { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using SiNan.Common.Log; |
||||
|
using SiNan.Common.Models; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace SiNan.API.Middlewares |
||||
|
{ |
||||
|
public class CustomExceptionMiddleWare |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 管道请求委托
|
||||
|
/// </summary>
|
||||
|
private RequestDelegate _next; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 需要处理的状态码字典
|
||||
|
/// </summary>
|
||||
|
private IDictionary<int, string> _exceptionStatusCodeDic; |
||||
|
|
||||
|
//private NLogManager nLogManager;
|
||||
|
|
||||
|
private NLogManager nLogManager; |
||||
|
|
||||
|
public CustomExceptionMiddleWare(RequestDelegate next, NLogManager nLogManager) |
||||
|
{ |
||||
|
_next = next; |
||||
|
//this.logger = logger;
|
||||
|
this.nLogManager = nLogManager; |
||||
|
_exceptionStatusCodeDic = new Dictionary<int, string> |
||||
|
{ |
||||
|
{ 401, "未授权的请求" }, |
||||
|
{ 404, "找不到该资源" }, |
||||
|
{ 403, "访问被拒绝" }, |
||||
|
{ 500, "服务器发生意外的错误" }, |
||||
|
{ 503, "服务不可用" } |
||||
|
//其余状态自行扩展
|
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public async Task Invoke(HttpContext context) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await _next(context); //调用管道执行下一个中间件
|
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
if (ex is BusinessException) |
||||
|
{ |
||||
|
var busEx = ex as BusinessException; |
||||
|
context.Response.StatusCode = 200; //业务异常时将Http状态码改为200
|
||||
|
await ErrorHandle(context, busEx.Code, busEx.Message); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
context.Response.Clear(); |
||||
|
context.Response.StatusCode = 500; //发生未捕获的异常,手动设置状态码
|
||||
|
//logger.Error(ex); //记录错误
|
||||
|
nLogManager.Default().Error(ex); |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if (_exceptionStatusCodeDic.TryGetValue(context.Response.StatusCode, out string exMsg)) |
||||
|
{ |
||||
|
await ErrorHandle(context, context.Response.StatusCode, exMsg); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理方式:返回Json格式
|
||||
|
/// </summary>
|
||||
|
/// <param name="context"></param>
|
||||
|
/// <param name="code"></param>
|
||||
|
/// <param name="exMsg"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private async Task ErrorHandle(HttpContext context, int code, string exMsg) |
||||
|
{ |
||||
|
var apiResponse = ApiResponse.Error(code, exMsg); |
||||
|
var serialzeStr = JsonConvert.SerializeObject(apiResponse); |
||||
|
context.Response.ContentType = "application/json"; |
||||
|
await context.Response.WriteAsync(serialzeStr, Encoding.UTF8); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
||||
|
<targets> |
||||
|
<target name="errorFile" xsi:type="File" fileName="${basedir}/logs/${logger}/error/${shortdate}.txt" |
||||
|
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}${onexception:${exception:format=tostring} ${newline}${stacktrace} ${newline}${newline}" |
||||
|
autoFlush="true"/> |
||||
|
<target name="infoFile" xsi:type="File" fileName="${basedir}/logs/${logger}/info/${shortdate}.txt" |
||||
|
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}" |
||||
|
autoFlush="true"/> |
||||
|
</targets> |
||||
|
<rules> |
||||
|
<logger name="*" level="Error" writeTo="errorFile"/> |
||||
|
<logger name="*" level="Info" writeTo="infoFile" /> |
||||
|
</rules> |
||||
|
</nlog> |
@ -0,0 +1,147 @@ |
|||||
|
using SiNan.API.Filters; |
||||
|
using SiNan.Business; |
||||
|
using SiNan.Common.Http; |
||||
|
using SiNan.Common.Log; |
||||
|
using Yitter.IdGenerator; |
||||
|
using Newtonsoft.Json.Serialization; |
||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer; |
||||
|
using Microsoft.IdentityModel.Tokens; |
||||
|
using System.Text; |
||||
|
using Microsoft.OpenApi.Models; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
var builder = WebApplication.CreateBuilder(args); |
||||
|
var services = builder.Services; |
||||
|
var configuration = builder.Configuration; |
||||
|
|
||||
|
services.AddMemoryCache(); |
||||
|
var idOption = new IdGeneratorOptions(1); |
||||
|
var idGenerator = new DefaultIdGenerator(idOption); |
||||
|
services.AddSingleton(typeof(IIdGenerator), idGenerator); |
||||
|
|
||||
|
var fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, configuration.GetConnectionString("BBWYCDB")).Build(); |
||||
|
services.AddSingleton(typeof(IFreeSql), fsql); |
||||
|
var fsql2 = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, configuration.GetConnectionString("MDSDB")).Build(); |
||||
|
|
||||
|
services.AddSingleton(new FreeSqlMultiDBManager() |
||||
|
{ |
||||
|
MDSfsql = fsql2, |
||||
|
BBWYCfsql = fsql |
||||
|
}); |
||||
|
|
||||
|
services.AddSingleton<NLogManager>(); |
||||
|
services.AddSingleton<RestApiService>(); |
||||
|
services.AddSingleton<TaskSchedulerManager>(); |
||||
|
services.AddMemoryCache(); |
||||
|
services.AddControllers(); |
||||
|
services.AddHttpContextAccessor(); |
||||
|
services.AddHttpClient(); |
||||
|
services.AddHttpClient("gzip").ConfigurePrimaryHttpMessageHandler(handler => new HttpClientHandler() |
||||
|
{ |
||||
|
AutomaticDecompression = System.Net.DecompressionMethods.GZip |
||||
|
}); |
||||
|
services.AddCors(options => |
||||
|
{ |
||||
|
options.AddPolicy("cors", p => |
||||
|
{ |
||||
|
p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
services.AddControllers(c => |
||||
|
{ |
||||
|
c.Filters.Add<ResultFilter>(); |
||||
|
c.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; |
||||
|
}).AddNewtonsoftJson(setupAction => |
||||
|
{ |
||||
|
setupAction.SerializerSettings.ContractResolver = new DefaultContractResolver(); |
||||
|
setupAction.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; |
||||
|
//setupAction.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
|
||||
|
//setupAction.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Include;
|
||||
|
}); |
||||
|
|
||||
|
services.AddEndpointsApiExplorer(); |
||||
|
services.AddSwaggerGen(c => |
||||
|
{ |
||||
|
c.SwaggerDoc("v1", new OpenApiInfo |
||||
|
{ |
||||
|
Version = "v1.0.0", |
||||
|
Title = "司南API", |
||||
|
Description = "注意事项\r\n1.返回参数名称采用大驼峰命名\r\n2.ApiResponse为基础返回对象(Code,Data,Message),接口中所有的返回值均属于Data属性\r\n3.正常返回Code=200" |
||||
|
}); |
||||
|
//JWT认证
|
||||
|
c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme |
||||
|
{ |
||||
|
Scheme = JwtBearerDefaults.AuthenticationScheme, |
||||
|
BearerFormat = "JWT", |
||||
|
Type = SecuritySchemeType.ApiKey, |
||||
|
Name = "Authorization", |
||||
|
In = ParameterLocation.Header, |
||||
|
Description = "Authorization:Bearer {your JWT token}<br/>", |
||||
|
}); |
||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement |
||||
|
{ |
||||
|
{ |
||||
|
new OpenApiSecurityScheme{Reference = new OpenApiReference |
||||
|
{ |
||||
|
Type = ReferenceType.SecurityScheme, |
||||
|
Id = JwtBearerDefaults.AuthenticationScheme |
||||
|
} |
||||
|
}, |
||||
|
new string[] { } |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
var executingAssembly = Assembly.GetExecutingAssembly(); |
||||
|
var assemblyNames = executingAssembly.GetReferencedAssemblies().Union(new AssemblyName[] { executingAssembly.GetName() }).ToArray(); |
||||
|
Array.ForEach(assemblyNames, (assemblyName) => |
||||
|
{ |
||||
|
//var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
|
var xmlFile = $"{assemblyName.Name}.xml"; |
||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); |
||||
|
if (!File.Exists(xmlPath)) |
||||
|
return; |
||||
|
c.IncludeXmlComments(xmlPath, true); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
var secret = configuration.GetSection("Secret").Value; |
||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, x => |
||||
|
{ |
||||
|
x.SaveToken = true; |
||||
|
x.RequireHttpsMetadata = false; |
||||
|
x.TokenValidationParameters = new TokenValidationParameters() |
||||
|
{ |
||||
|
ClockSkew = TimeSpan.Zero, |
||||
|
ValidateIssuerSigningKey = true, |
||||
|
ValidateIssuer = false, |
||||
|
ValidateAudience = false, |
||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)), |
||||
|
//ValidIssuer = issuer,
|
||||
|
//ValidAudience = audience,
|
||||
|
//ValidateLifetime = true
|
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
|
||||
|
var app = builder.Build(); |
||||
|
|
||||
|
// Configure the HTTP request pipeline.
|
||||
|
if (!app.Environment.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseExceptionHandler("/Error"); |
||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
|
app.UseHsts(); |
||||
|
} |
||||
|
|
||||
|
app.UseHttpsRedirection(); |
||||
|
app.UseStaticFiles(); |
||||
|
|
||||
|
app.UseRouting(); |
||||
|
|
||||
|
app.UseAuthorization(); |
||||
|
|
||||
|
app.MapRazorPages(); |
||||
|
|
||||
|
app.Run(); |
@ -0,0 +1,30 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="AutoMapper" Version="12.0.1" /> |
||||
|
<PackageReference Include="FreeSql" Version="3.2.801" /> |
||||
|
<PackageReference Include="FreeSql.Provider.MySql" Version="3.2.801" /> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.23" /> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.23" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> |
||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |
||||
|
<PackageReference Include="NLog" Version="5.2.5" /> |
||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> |
||||
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\SiNan.Business\SiNan.Business.csproj" /> |
||||
|
<ProjectReference Include="..\SiNan.Common\SiNan.Common.csproj" /> |
||||
|
<ProjectReference Include="..\SiNan.Model\SiNan.Model.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
@ -1,25 +0,0 @@ |
|||||
|
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|
||||
# Visual Studio Version 17 |
|
||||
VisualStudioVersion = 17.7.34024.191 |
|
||||
MinimumVisualStudioVersion = 10.0.40219.1 |
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SiNan.API", "SiNan.API\SiNan.API.csproj", "{33F844AA-8786-4294-8F5A-2E5ED03D56FF}" |
|
||||
EndProject |
|
||||
Global |
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|
||||
Debug|Any CPU = Debug|Any CPU |
|
||||
Release|Any CPU = Release|Any CPU |
|
||||
EndGlobalSection |
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|
||||
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|
||||
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|
||||
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|
||||
{33F844AA-8786-4294-8F5A-2E5ED03D56FF}.Release|Any CPU.Build.0 = Release|Any CPU |
|
||||
EndGlobalSection |
|
||||
GlobalSection(SolutionProperties) = preSolution |
|
||||
HideSolutionNode = FALSE |
|
||||
EndGlobalSection |
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution |
|
||||
SolutionGuid = {A3B189CF-4897-44E3-A2E2-BB445F7512FD} |
|
||||
EndGlobalSection |
|
||||
EndGlobal |
|
@ -1,26 +0,0 @@ |
|||||
@page |
|
||||
@model ErrorModel |
|
||||
@{ |
|
||||
ViewData["Title"] = "Error"; |
|
||||
} |
|
||||
|
|
||||
<h1 class="text-danger">Error.</h1> |
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2> |
|
||||
|
|
||||
@if (Model.ShowRequestId) |
|
||||
{ |
|
||||
<p> |
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code> |
|
||||
</p> |
|
||||
} |
|
||||
|
|
||||
<h3>Development Mode</h3> |
|
||||
<p> |
|
||||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred. |
|
||||
</p> |
|
||||
<p> |
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong> |
|
||||
It can result in displaying sensitive information from exceptions to end users. |
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> |
|
||||
and restarting the app. |
|
||||
</p> |
|
@ -1,27 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|
||||
using System.Diagnostics; |
|
||||
|
|
||||
namespace SiNan.API.Pages |
|
||||
{ |
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] |
|
||||
[IgnoreAntiforgeryToken] |
|
||||
public class ErrorModel : PageModel |
|
||||
{ |
|
||||
public string? RequestId { get; set; } |
|
||||
|
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); |
|
||||
|
|
||||
private readonly ILogger<ErrorModel> _logger; |
|
||||
|
|
||||
public ErrorModel(ILogger<ErrorModel> logger) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public void OnGet() |
|
||||
{ |
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
@page |
|
||||
@model IndexModel |
|
||||
@{ |
|
||||
ViewData["Title"] = "Home page"; |
|
||||
} |
|
||||
|
|
||||
<div class="text-center"> |
|
||||
<h1 class="display-4">Welcome</h1> |
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> |
|
||||
</div> |
|
@ -1,20 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|
||||
|
|
||||
namespace SiNan.API.Pages |
|
||||
{ |
|
||||
public class IndexModel : PageModel |
|
||||
{ |
|
||||
private readonly ILogger<IndexModel> _logger; |
|
||||
|
|
||||
public IndexModel(ILogger<IndexModel> logger) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public void OnGet() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
@page |
|
||||
@model PrivacyModel |
|
||||
@{ |
|
||||
ViewData["Title"] = "Privacy Policy"; |
|
||||
} |
|
||||
<h1>@ViewData["Title"]</h1> |
|
||||
|
|
||||
<p>Use this page to detail your site's privacy policy.</p> |
|
@ -1,19 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|
||||
|
|
||||
namespace SiNan.API.Pages |
|
||||
{ |
|
||||
public class PrivacyModel : PageModel |
|
||||
{ |
|
||||
private readonly ILogger<PrivacyModel> _logger; |
|
||||
|
|
||||
public PrivacyModel(ILogger<PrivacyModel> logger) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public void OnGet() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
<!DOCTYPE html> |
|
||||
<html lang="en"> |
|
||||
<head> |
|
||||
<meta charset="utf-8" /> |
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
||||
<title>@ViewData["Title"] - SiNan.API</title> |
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> |
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> |
|
||||
<link rel="stylesheet" href="~/SiNan.API.styles.css" asp-append-version="true" /> |
|
||||
</head> |
|
||||
<body> |
|
||||
<header> |
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> |
|
||||
<div class="container"> |
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">SiNan.API</a> |
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" |
|
||||
aria-expanded="false" aria-label="Toggle navigation"> |
|
||||
<span class="navbar-toggler-icon"></span> |
|
||||
</button> |
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> |
|
||||
<ul class="navbar-nav flex-grow-1"> |
|
||||
<li class="nav-item"> |
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a> |
|
||||
</li> |
|
||||
<li class="nav-item"> |
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a> |
|
||||
</li> |
|
||||
</ul> |
|
||||
</div> |
|
||||
</div> |
|
||||
</nav> |
|
||||
</header> |
|
||||
<div class="container"> |
|
||||
<main role="main" class="pb-3"> |
|
||||
@RenderBody() |
|
||||
</main> |
|
||||
</div> |
|
||||
|
|
||||
<footer class="border-top footer text-muted"> |
|
||||
<div class="container"> |
|
||||
© 2023 - SiNan.API - <a asp-area="" asp-page="/Privacy">Privacy</a> |
|
||||
</div> |
|
||||
</footer> |
|
||||
|
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script> |
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> |
|
||||
<script src="~/js/site.js" asp-append-version="true"></script> |
|
||||
|
|
||||
@await RenderSectionAsync("Scripts", required: false) |
|
||||
</body> |
|
||||
</html> |
|
@ -1,48 +0,0 @@ |
|||||
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification |
|
||||
for details on configuring this project to bundle and minify static web assets. */ |
|
||||
|
|
||||
a.navbar-brand { |
|
||||
white-space: normal; |
|
||||
text-align: center; |
|
||||
word-break: break-all; |
|
||||
} |
|
||||
|
|
||||
a { |
|
||||
color: #0077cc; |
|
||||
} |
|
||||
|
|
||||
.btn-primary { |
|
||||
color: #fff; |
|
||||
background-color: #1b6ec2; |
|
||||
border-color: #1861ac; |
|
||||
} |
|
||||
|
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link { |
|
||||
color: #fff; |
|
||||
background-color: #1b6ec2; |
|
||||
border-color: #1861ac; |
|
||||
} |
|
||||
|
|
||||
.border-top { |
|
||||
border-top: 1px solid #e5e5e5; |
|
||||
} |
|
||||
.border-bottom { |
|
||||
border-bottom: 1px solid #e5e5e5; |
|
||||
} |
|
||||
|
|
||||
.box-shadow { |
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); |
|
||||
} |
|
||||
|
|
||||
button.accept-policy { |
|
||||
font-size: 1rem; |
|
||||
line-height: inherit; |
|
||||
} |
|
||||
|
|
||||
.footer { |
|
||||
position: absolute; |
|
||||
bottom: 0; |
|
||||
width: 100%; |
|
||||
white-space: nowrap; |
|
||||
line-height: 60px; |
|
||||
} |
|
@ -1,2 +0,0 @@ |
|||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> |
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> |
|
@ -1,3 +0,0 @@ |
|||||
@using SiNan.API |
|
||||
@namespace SiNan.API.Pages |
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
|
@ -1,3 +0,0 @@ |
|||||
@{ |
|
||||
Layout = "_Layout"; |
|
||||
} |
|
@ -1,25 +0,0 @@ |
|||||
var builder = WebApplication.CreateBuilder(args); |
|
||||
|
|
||||
// Add services to the container.
|
|
||||
builder.Services.AddRazorPages(); |
|
||||
|
|
||||
var app = builder.Build(); |
|
||||
|
|
||||
// Configure the HTTP request pipeline.
|
|
||||
if (!app.Environment.IsDevelopment()) |
|
||||
{ |
|
||||
app.UseExceptionHandler("/Error"); |
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||
app.UseHsts(); |
|
||||
} |
|
||||
|
|
||||
app.UseHttpsRedirection(); |
|
||||
app.UseStaticFiles(); |
|
||||
|
|
||||
app.UseRouting(); |
|
||||
|
|
||||
app.UseAuthorization(); |
|
||||
|
|
||||
app.MapRazorPages(); |
|
||||
|
|
||||
app.Run(); |
|
@ -1,9 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>net6.0</TargetFramework> |
|
||||
<Nullable>enable</Nullable> |
|
||||
<ImplicitUsings>enable</ImplicitUsings> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
</Project> |
|
@ -1,9 +0,0 @@ |
|||||
{ |
|
||||
"Logging": { |
|
||||
"LogLevel": { |
|
||||
"Default": "Information", |
|
||||
"Microsoft.AspNetCore": "Warning" |
|
||||
} |
|
||||
}, |
|
||||
"AllowedHosts": "*" |
|
||||
} |
|
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.AspNetCore": "Warning" |
||||
|
} |
||||
|
}, |
||||
|
"AllowedHosts": "*", |
||||
|
"ConnectionStrings": { |
||||
|
//"DB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=bbwy;charset=utf8;sslmode=none;" |
||||
|
"BBWYCDB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=bbwy_test;charset=utf8;sslmode=none;", |
||||
|
"MDSDB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=mds;charset=utf8;sslmode=none;" |
||||
|
} |
||||
|
} |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
@ -0,0 +1,11 @@ |
|||||
|
namespace SiNan.Business |
||||
|
{ |
||||
|
public class FreeSqlMultiDBManager |
||||
|
{ |
||||
|
//public IFreeSql BBWYBfsql { get; set; }
|
||||
|
|
||||
|
public IFreeSql MDSfsql { get; set; } |
||||
|
|
||||
|
public IFreeSql BBWYCfsql { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="AutoMapper" Version="12.0.1" /> |
||||
|
<PackageReference Include="FreeSql" Version="3.2.801" /> |
||||
|
<PackageReference Include="FreeSql.Provider.MySql" Version="3.2.801" /> |
||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |
||||
|
<PackageReference Include="NLog" Version="5.2.5" /> |
||||
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\SiNan.Common\SiNan.Common.csproj" /> |
||||
|
<ProjectReference Include="..\SiNan.Model\SiNan.Model.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,20 @@ |
|||||
|
using System.Threading.Tasks.Schedulers; |
||||
|
|
||||
|
namespace SiNan.Business |
||||
|
{ |
||||
|
public class TaskSchedulerManager |
||||
|
{ |
||||
|
public LimitedConcurrencyLevelTaskScheduler SyncProductTaskScheduler { get; private set; } |
||||
|
public LimitedConcurrencyLevelTaskScheduler SyncOrderTaskScheduler { get; private set; } |
||||
|
|
||||
|
public LimitedConcurrencyLevelTaskScheduler PurchaseOrderCallbackTaskScheduler { get; private set; } |
||||
|
|
||||
|
|
||||
|
public TaskSchedulerManager() |
||||
|
{ |
||||
|
SyncOrderTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); |
||||
|
PurchaseOrderCallbackTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); |
||||
|
SyncProductTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class ConverterExtensions |
||||
|
{ |
||||
|
public static long? ToInt64(this object? o) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
return Convert.ToInt64(o); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static int? ToInt32(this object? o) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
return Convert.ToInt32(o); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class CopyExtensions |
||||
|
{ |
||||
|
public static T Copy<T>(this T p) |
||||
|
{ |
||||
|
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(p)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,99 @@ |
|||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class DateTimeExtension |
||||
|
{ |
||||
|
private static readonly DateTime beginTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间戳转时间
|
||||
|
/// </summary>
|
||||
|
/// <param name="timeStamp">时间</param>
|
||||
|
/// <param name="len13">true:13, false:10</param>
|
||||
|
/// <returns></returns>
|
||||
|
[Obsolete] |
||||
|
public static DateTime StampToDateTime(this long timeStamp) |
||||
|
{ |
||||
|
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(beginTime); |
||||
|
return timeStamp.ToString().Length == 13 ? dt.AddMilliseconds(timeStamp) : dt.AddSeconds(timeStamp); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间转时间戳
|
||||
|
/// </summary>
|
||||
|
/// <param name="time">时间</param>
|
||||
|
/// <param name="len13">true:13, false:10</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static long DateTimeToStamp(this DateTime time, bool len13 = true) |
||||
|
{ |
||||
|
TimeSpan ts = time.ToUniversalTime() - beginTime; |
||||
|
if (len13) |
||||
|
return Convert.ToInt64(ts.TotalMilliseconds); |
||||
|
else |
||||
|
return Convert.ToInt64(ts.TotalSeconds); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 将秒数转换为时分秒形式
|
||||
|
/// </summary>
|
||||
|
/// <param name="second"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string FormatToHHmmss(long second) |
||||
|
{ |
||||
|
if (second < 60) |
||||
|
return $"00:00:{(second >= 10 ? $"{second}" : $"0{second}")}"; |
||||
|
if (second < 3600) |
||||
|
{ |
||||
|
var minute = second / 60; |
||||
|
var s = second % 60; |
||||
|
return $"00:{(minute >= 10 ? $"{minute}" : $"0{minute}")}:{(s >= 10 ? $"{s}" : $"0{s}")}"; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var hour = second / 3600; |
||||
|
var minute = (second - (hour * 3600)) / 60; |
||||
|
var s = (second - ((hour * 3600) + minute * 60)) % 60; |
||||
|
return $"{(hour >= 10 ? $"{hour}" : $"0{hour}")}:{(minute >= 10 ? $"{minute}" : $"0{minute}")}:{(s >= 10 ? $"{s}" : $"0{s}")}"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#region SetLocalTime
|
||||
|
[DllImport("Kernel32.dll")] |
||||
|
private static extern bool SetLocalTime(ref SYSTEMTIME lpSystemTime); |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
private struct SYSTEMTIME |
||||
|
{ |
||||
|
public ushort wYear; |
||||
|
public ushort wMonth; |
||||
|
public ushort wDayOfWeek; |
||||
|
public ushort wDay; |
||||
|
public ushort wHour; |
||||
|
public ushort wMinute; |
||||
|
public ushort wSecond; |
||||
|
public ushort wMilliseconds; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 修改系统时间(需要管理员权限)
|
||||
|
/// </summary>
|
||||
|
/// <param name="date"></param>
|
||||
|
public static void SetSystemTime(DateTime date) |
||||
|
{ |
||||
|
SYSTEMTIME lpTime = new SYSTEMTIME(); |
||||
|
lpTime.wYear = Convert.ToUInt16(date.Year); |
||||
|
lpTime.wMonth = Convert.ToUInt16(date.Month); |
||||
|
lpTime.wDayOfWeek = Convert.ToUInt16(date.DayOfWeek); |
||||
|
lpTime.wDay = Convert.ToUInt16(date.Day); |
||||
|
DateTime time = date; |
||||
|
lpTime.wHour = Convert.ToUInt16(time.Hour); |
||||
|
lpTime.wMinute = Convert.ToUInt16(time.Minute); |
||||
|
lpTime.wSecond = Convert.ToUInt16(time.Second); |
||||
|
lpTime.wMilliseconds = Convert.ToUInt16(time.Millisecond); |
||||
|
var r = SetLocalTime(ref lpTime); |
||||
|
Console.WriteLine($"修改系统时间 {r}"); |
||||
|
} |
||||
|
#endregion
|
||||
|
} |
||||
|
} |
@ -0,0 +1,82 @@ |
|||||
|
using System.Security.Cryptography; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class EncryptionExtension |
||||
|
{ |
||||
|
|
||||
|
public static string Md5Encrypt(this string originStr) |
||||
|
{ |
||||
|
using (var md5 = MD5.Create()) |
||||
|
{ |
||||
|
return string.Join(string.Empty, md5.ComputeHash(Encoding.UTF8.GetBytes(originStr)).Select(x => x.ToString("x2"))); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//AES加密 传入,要加密的串和, 解密key
|
||||
|
public static string AESEncrypt(this string input) |
||||
|
{ |
||||
|
var key = "dataplatform2019"; |
||||
|
var ivStr = "1012132405963708"; |
||||
|
|
||||
|
var encryptKey = Encoding.UTF8.GetBytes(key); |
||||
|
var iv = Encoding.UTF8.GetBytes(ivStr); //偏移量,最小为16
|
||||
|
using (var aesAlg = Aes.Create()) |
||||
|
{ |
||||
|
using (var encryptor = aesAlg.CreateEncryptor(encryptKey, iv)) |
||||
|
{ |
||||
|
using (var msEncrypt = new MemoryStream()) |
||||
|
{ |
||||
|
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, |
||||
|
CryptoStreamMode.Write)) |
||||
|
|
||||
|
using (var swEncrypt = new StreamWriter(csEncrypt)) |
||||
|
{ |
||||
|
swEncrypt.Write(input); |
||||
|
} |
||||
|
var decryptedContent = msEncrypt.ToArray(); |
||||
|
|
||||
|
return Convert.ToBase64String(decryptedContent); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static string AESDecrypt(this string cipherText) |
||||
|
{ |
||||
|
var fullCipher = Convert.FromBase64String(cipherText); |
||||
|
|
||||
|
var ivStr = "1012132405963708"; |
||||
|
var key = "dataplatform2019"; |
||||
|
|
||||
|
var iv = Encoding.UTF8.GetBytes(ivStr); |
||||
|
var decryptKey = Encoding.UTF8.GetBytes(key); |
||||
|
|
||||
|
using (var aesAlg = Aes.Create()) |
||||
|
{ |
||||
|
using (var decryptor = aesAlg.CreateDecryptor(decryptKey, iv)) |
||||
|
{ |
||||
|
string result; |
||||
|
using (var msDecrypt = new MemoryStream(fullCipher)) |
||||
|
{ |
||||
|
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) |
||||
|
{ |
||||
|
using (var srDecrypt = new StreamReader(csDecrypt)) |
||||
|
{ |
||||
|
result = srDecrypt.ReadToEnd(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static string Base64Encrypt(this string originStr) |
||||
|
{ |
||||
|
return Convert.ToBase64String(Encoding.UTF8.GetBytes(originStr)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class MapperExtension |
||||
|
{ |
||||
|
private static IMapper _mapper; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 注册对象映射器
|
||||
|
/// </summary>
|
||||
|
/// <param name="services"></param>
|
||||
|
/// <param name="profile"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static IServiceCollection AddMapper(this IServiceCollection services, Profile profile) |
||||
|
{ |
||||
|
var config = new MapperConfiguration(cfg => |
||||
|
{ |
||||
|
cfg.AddProfile(profile); |
||||
|
}); |
||||
|
_mapper = config.CreateMapper(); |
||||
|
return services; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 设置对象映射执行者
|
||||
|
/// </summary>
|
||||
|
/// <param name="mapper">映射执行者</param>
|
||||
|
public static void SetMapper(IMapper mapper) |
||||
|
{ |
||||
|
_mapper = mapper; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 将对象映射为指定类型
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TTarget">要映射的目标类型</typeparam>
|
||||
|
/// <param name="source">源对象</param>
|
||||
|
/// <returns>目标类型的对象</returns>
|
||||
|
public static TTarget Map<TTarget>(this object source) |
||||
|
{ |
||||
|
return _mapper.Map<TTarget>(source); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 使用源类型的对象更新目标类型的对象
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSource">源类型</typeparam>
|
||||
|
/// <typeparam name="TTarget">目标类型</typeparam>
|
||||
|
/// <param name="source">源对象</param>
|
||||
|
/// <param name="target">待更新的目标对象</param>
|
||||
|
/// <returns>更新后的目标类型对象</returns>
|
||||
|
public static TTarget Map<TSource, TTarget>(this TSource source, TTarget target) |
||||
|
{ |
||||
|
return _mapper.Map(source, target); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace SiNan.Common.Extensions |
||||
|
{ |
||||
|
public static class StartupExtension |
||||
|
{ |
||||
|
public static void BatchRegisterServices(this IServiceCollection serviceCollection, Assembly[] assemblys, Type baseType, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, bool registerSelf = true) |
||||
|
{ |
||||
|
List<Type> typeList = new List<Type>(); //所有符合注册条件的类集合
|
||||
|
foreach (var assembly in assemblys) |
||||
|
{ |
||||
|
var types = assembly.GetTypes().Where(t => t.IsClass && !t.IsSealed && !t.IsAbstract && baseType.IsAssignableFrom(t)); |
||||
|
typeList.AddRange(types); |
||||
|
} |
||||
|
|
||||
|
if (typeList.Count() == 0) |
||||
|
return; |
||||
|
|
||||
|
foreach (var instanceType in typeList) |
||||
|
{ |
||||
|
if (registerSelf) |
||||
|
{ |
||||
|
serviceCollection.Add(new ServiceDescriptor(instanceType, instanceType, serviceLifetime)); |
||||
|
continue; |
||||
|
} |
||||
|
var interfaces = instanceType.GetInterfaces(); |
||||
|
if (interfaces != null && interfaces.Length > 0) |
||||
|
foreach (var interfaceType in interfaces) |
||||
|
serviceCollection.Add(new ServiceDescriptor(interfaceType, instanceType, serviceLifetime)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,452 @@ |
|||||
|
using System.Net; |
||||
|
|
||||
|
namespace SiNan.Common.Http |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Http下载器
|
||||
|
/// </summary>
|
||||
|
public class HttpDownloader |
||||
|
{ |
||||
|
|
||||
|
private IHttpClientFactory httpClientFactory; |
||||
|
|
||||
|
public HttpDownloader(IHttpClientFactory httpClientFactory, DownloadSetting dwSetting = null) |
||||
|
{ |
||||
|
this.httpClientFactory = httpClientFactory; |
||||
|
complateArgs = new DownloadCompletedEventArgs(); |
||||
|
changeArgs = new DownloadProgressChangedEventArgs(); |
||||
|
if (dwSetting == null) |
||||
|
dwSetting = new DownloadSetting(); |
||||
|
downloadSetting = dwSetting; |
||||
|
buffer = new byte[downloadSetting.BufferSize]; |
||||
|
} |
||||
|
|
||||
|
~HttpDownloader() |
||||
|
{ |
||||
|
this.buffer = null; |
||||
|
this.downloadSetting = null; |
||||
|
this.complateArgs.Error = null; |
||||
|
this.complateArgs = null; |
||||
|
this.changeArgs = null; |
||||
|
} |
||||
|
|
||||
|
#region Property
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载进度变化参数
|
||||
|
/// </summary>
|
||||
|
private DownloadProgressChangedEventArgs changeArgs; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载完成参数
|
||||
|
/// </summary>
|
||||
|
private DownloadCompletedEventArgs complateArgs; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载参数配置类
|
||||
|
/// </summary>
|
||||
|
private DownloadSetting downloadSetting; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载缓冲区
|
||||
|
/// </summary>
|
||||
|
private byte[] buffer; |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region Delegate
|
||||
|
public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e); |
||||
|
|
||||
|
public delegate void DownloadCompletedEventHandler(object sender, DownloadCompletedEventArgs e); |
||||
|
|
||||
|
public delegate void ReDownloadEventHandler(object sender, DownloadCompletedEventArgs e); |
||||
|
#endregion
|
||||
|
|
||||
|
#region Event
|
||||
|
/// <summary>
|
||||
|
/// 下载进度变化事件
|
||||
|
/// </summary>
|
||||
|
public event DownloadProgressChangedEventHandler OnDownloadProgressChanged; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载完成事件
|
||||
|
/// </summary>
|
||||
|
public event DownloadCompletedEventHandler OnDownloadComplated; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 自动重下事件
|
||||
|
/// </summary>
|
||||
|
public event ReDownloadEventHandler OnReDownload; |
||||
|
#endregion
|
||||
|
|
||||
|
#region Method
|
||||
|
/// <summary>
|
||||
|
/// 设置下载参数
|
||||
|
/// </summary>
|
||||
|
/// <param name="dwSetting"></param>
|
||||
|
public void SetDownloadSetting(DownloadSetting dwSetting) |
||||
|
{ |
||||
|
if (dwSetting != null) |
||||
|
downloadSetting = dwSetting; |
||||
|
} |
||||
|
|
||||
|
private void DoProcessChangedEvent(DownloadProgressChangedEventArgs args) |
||||
|
{ |
||||
|
OnDownloadProgressChanged?.Invoke(this, args); |
||||
|
} |
||||
|
|
||||
|
private void DoCompletedEvent(DownloadCompletedEventArgs args) |
||||
|
{ |
||||
|
OnDownloadComplated?.Invoke(this, args); |
||||
|
} |
||||
|
|
||||
|
private void DoReDownloadEvent(DownloadCompletedEventArgs args) |
||||
|
{ |
||||
|
OnReDownload?.Invoke(this, args); |
||||
|
} |
||||
|
|
||||
|
private void ArgsInit() |
||||
|
{ |
||||
|
changeArgs.Cancel = false; |
||||
|
complateArgs.Cancelled = false; |
||||
|
complateArgs.Error = null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取文件总大小
|
||||
|
/// </summary>
|
||||
|
/// <param name="url"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public long GetTotalLength(string url) |
||||
|
{ |
||||
|
long length = 0; |
||||
|
try |
||||
|
{ |
||||
|
using (var httpClient = httpClientFactory.CreateClient()) |
||||
|
{ |
||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Head, url); |
||||
|
var httpResponse = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead).Result; |
||||
|
if (httpResponse.IsSuccessStatusCode) |
||||
|
length = httpResponse.Content.Headers.ContentLength ?? 0; |
||||
|
} |
||||
|
|
||||
|
//var req = (HttpWebRequest)WebRequest.Create(new Uri(url));
|
||||
|
//req.Method = "HEAD";
|
||||
|
//req.Timeout = 5000;
|
||||
|
//var res = (HttpWebResponse)req.GetResponse();
|
||||
|
//if (res.StatusCode == HttpStatusCode.OK)
|
||||
|
//{
|
||||
|
// length = res.ContentLength;
|
||||
|
//}
|
||||
|
//res.Close();
|
||||
|
return length; |
||||
|
} |
||||
|
catch (WebException wex) |
||||
|
{ |
||||
|
throw wex; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private bool DownloadFile(string url, string savePath, long startPosition, long totalSize) |
||||
|
{ |
||||
|
long currentReadSize = 0; //当前已经读取的字节数
|
||||
|
if (startPosition != 0) |
||||
|
currentReadSize = startPosition; |
||||
|
using (var httpClient = httpClientFactory.CreateClient()) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
httpClient.Timeout = new TimeSpan(0, 0, 20); |
||||
|
if (currentReadSize != 0 && currentReadSize == totalSize) |
||||
|
{ |
||||
|
changeArgs.CurrentSize = changeArgs.TotalSize = totalSize; |
||||
|
return true; |
||||
|
} |
||||
|
if (currentReadSize != 0) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
httpClient.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(currentReadSize, totalSize); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
//http 416
|
||||
|
currentReadSize = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
using (var response = httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result) |
||||
|
{ |
||||
|
using (var responseStream = response.Content.ReadAsStreamAsync().Result) |
||||
|
{ |
||||
|
changeArgs.TotalSize = totalSize; |
||||
|
using (var fs = new FileStream(savePath, currentReadSize == 0 ? FileMode.Create : FileMode.Append, FileAccess.Write)) |
||||
|
{ |
||||
|
//判断服务器是否支持断点续下
|
||||
|
if (currentReadSize != 0 && |
||||
|
response.StatusCode == HttpStatusCode.PartialContent && |
||||
|
response.Content.Headers.ContentLength != totalSize) |
||||
|
fs.Seek(startPosition, SeekOrigin.Begin); //支持,从本地文件流末尾进行写入
|
||||
|
else |
||||
|
currentReadSize = 0; //不支持,从本地文件流开始进行写入
|
||||
|
|
||||
|
if (buffer.Length != downloadSetting.BufferSize) |
||||
|
buffer = new byte[downloadSetting.BufferSize]; |
||||
|
int size = responseStream.Read(buffer, 0, buffer.Length); |
||||
|
while (size != 0) |
||||
|
{ |
||||
|
fs.Write(buffer, 0, size); |
||||
|
if (buffer.Length != downloadSetting.BufferSize) |
||||
|
buffer = new byte[downloadSetting.BufferSize]; |
||||
|
size = responseStream.Read(buffer, 0, buffer.Length); |
||||
|
currentReadSize += size; //累计下载大小
|
||||
|
//执行下载进度变化事件
|
||||
|
changeArgs.CurrentSize = currentReadSize; |
||||
|
DoProcessChangedEvent(changeArgs); |
||||
|
//判断挂起状态
|
||||
|
if (changeArgs.Cancel) |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//执行下载进度变化事件
|
||||
|
changeArgs.CurrentSize = changeArgs.TotalSize; |
||||
|
DoProcessChangedEvent(changeArgs); |
||||
|
fs.Flush(true); |
||||
|
} |
||||
|
|
||||
|
//验证远程服务器文件字节数和本地文件字节数是否一致
|
||||
|
long localFileSize = 0; |
||||
|
try |
||||
|
{ |
||||
|
localFileSize = new FileInfo(savePath).Length; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
localFileSize = changeArgs.CurrentSize; |
||||
|
} |
||||
|
|
||||
|
if (totalSize != localFileSize) |
||||
|
{ |
||||
|
complateArgs.Error = new Exception(string.Format("远程文件字节:[{0}]与本地文件字节:[{1}]不一致", totalSize, localFileSize)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (IOException ex) |
||||
|
{ |
||||
|
complateArgs.Error = ex; |
||||
|
} |
||||
|
catch (WebException ex) |
||||
|
{ |
||||
|
complateArgs.Error = ex; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
complateArgs.Error = ex; |
||||
|
} |
||||
|
} |
||||
|
return complateArgs.Error == null; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public bool DownloadFile(string url, string saveFolderPath, string saveFileName, long? totalSize) |
||||
|
{ |
||||
|
ArgsInit(); |
||||
|
var result = false; |
||||
|
//验证文件夹
|
||||
|
try |
||||
|
{ |
||||
|
if (!Directory.Exists(saveFolderPath)) |
||||
|
Directory.CreateDirectory(saveFolderPath); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
complateArgs.Error = ex; |
||||
|
DoCompletedEvent(complateArgs); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
if (totalSize == null || totalSize.Value == 0) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
totalSize = GetTotalLength(url); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Console.WriteLine("获取远程服务器字节数失败 {0}", ex.Message); |
||||
|
complateArgs.Error = ex; |
||||
|
DoCompletedEvent(complateArgs); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string saveFilePath = Path.Combine(saveFolderPath, saveFileName); |
||||
|
long startPosition = 0; |
||||
|
for (var i = 1; i <= downloadSetting.TryCount; i++) |
||||
|
{ |
||||
|
if (File.Exists(saveFilePath)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
startPosition = new FileInfo(saveFilePath).Length; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
startPosition = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result = DownloadFile(url, saveFilePath, startPosition, totalSize.Value); |
||||
|
if (result) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (i != downloadSetting.TryCount) |
||||
|
{ |
||||
|
complateArgs.TryCount = i + 1; |
||||
|
DoReDownloadEvent(complateArgs); |
||||
|
Thread.Sleep(downloadSetting.SleepTime); |
||||
|
if (complateArgs.Cancelled) |
||||
|
break; |
||||
|
ArgsInit(); |
||||
|
} |
||||
|
} |
||||
|
if (complateArgs.Cancelled) |
||||
|
break; |
||||
|
} |
||||
|
DoCompletedEvent(complateArgs); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public byte[] DwonloadBytes(string url) |
||||
|
{ |
||||
|
ArgsInit(); |
||||
|
using (var httpClient = httpClientFactory.CreateClient()) |
||||
|
{ |
||||
|
return httpClient.GetByteArrayAsync(url).Result; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 取消/暂停下载
|
||||
|
/// </summary>
|
||||
|
public void CancelDownload() |
||||
|
{ |
||||
|
this.changeArgs.Cancel = true; |
||||
|
this.complateArgs.Cancelled = true; |
||||
|
} |
||||
|
#endregion
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载进度变化参数
|
||||
|
/// </summary>
|
||||
|
public class DownloadProgressChangedEventArgs |
||||
|
{ |
||||
|
public DownloadProgressChangedEventArgs() |
||||
|
{ |
||||
|
ProgressPercentage = 0.0; |
||||
|
Cancel = false; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 下载进度百分比
|
||||
|
/// </summary>
|
||||
|
public double ProgressPercentage; |
||||
|
|
||||
|
|
||||
|
private long currentSize; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 当前下载总大小
|
||||
|
/// </summary>
|
||||
|
public long CurrentSize |
||||
|
{ |
||||
|
get { return currentSize; } |
||||
|
set |
||||
|
{ |
||||
|
currentSize = value; |
||||
|
this.ProgressPercentage = Math.Round((CurrentSize * 1.0 / TotalSize) * 100, 2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 文件总大小
|
||||
|
/// </summary>
|
||||
|
public long TotalSize; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 取消状态
|
||||
|
/// </summary>
|
||||
|
public bool Cancel; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载完成参数
|
||||
|
/// </summary>
|
||||
|
public class DownloadCompletedEventArgs |
||||
|
{ |
||||
|
public DownloadCompletedEventArgs() |
||||
|
{ |
||||
|
Cancelled = false; |
||||
|
Error = null; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 下载异常
|
||||
|
/// </summary>
|
||||
|
public Exception Error; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 重试次数
|
||||
|
/// </summary>
|
||||
|
public int TryCount; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载操作是否被取消 【取消则为true;默认false】
|
||||
|
/// </summary>
|
||||
|
public bool Cancelled; |
||||
|
} |
||||
|
|
||||
|
public class DownloadSetting |
||||
|
{ |
||||
|
public DownloadSetting() |
||||
|
{ |
||||
|
this.BufferSize = 1024 * 1024 * 1; |
||||
|
this.TryCount = 5; |
||||
|
this.SleepTime = 5000; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
///
|
||||
|
/// </summary>
|
||||
|
/// <param name="bufferSize"></param>
|
||||
|
/// <param name="tryCount"></param>
|
||||
|
/// <param name="sleepTime">毫秒</param>
|
||||
|
public DownloadSetting(int bufferSize, int tryCount, int sleepTime) |
||||
|
{ |
||||
|
this.BufferSize = bufferSize; |
||||
|
this.TryCount = tryCount; |
||||
|
this.SleepTime = sleepTime; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下载缓冲区大小
|
||||
|
/// </summary>
|
||||
|
public int BufferSize; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 重试次数
|
||||
|
/// </summary>
|
||||
|
public int TryCount; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 自动重下休眠时间, 毫秒
|
||||
|
/// </summary>
|
||||
|
public int SleepTime; |
||||
|
} |
||||
|
} |
@ -0,0 +1,117 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using SiNan.Common.Extensions; |
||||
|
using System.Net; |
||||
|
using System.Net.Http.Headers; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace SiNan.Common.Http |
||||
|
{ |
||||
|
public class RestApiService |
||||
|
{ |
||||
|
public const string ContentType_Json = "application/json"; |
||||
|
public const string ContentType_Form = "application/x-www-form-urlencoded"; |
||||
|
public TimeSpan TimeOut { get; set; } = new TimeSpan(0, 0, 40); |
||||
|
|
||||
|
private IHttpClientFactory httpClientFactory; |
||||
|
|
||||
|
public RestApiService(IHttpClientFactory httpClientFactory) |
||||
|
{ |
||||
|
this.httpClientFactory = httpClientFactory; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发送请求
|
||||
|
/// </summary>
|
||||
|
/// <param name="apiHost"></param>
|
||||
|
/// <param name="apiPath"></param>
|
||||
|
/// <param name="param"></param>
|
||||
|
/// <param name="requestHeaders"></param>
|
||||
|
/// <param name="httpMethod"></param>
|
||||
|
/// <param name="contentType"></param>
|
||||
|
/// <param name="paramPosition"></param>
|
||||
|
/// <param name="enableRandomTimeStamp"></param>
|
||||
|
/// <param name="getResponseHeader"></param>
|
||||
|
/// <param name="httpCompletionOption"></param>
|
||||
|
/// <param name="httpClientName"></param>
|
||||
|
/// <returns></returns>
|
||||
|
/// <exception cref="Exception"></exception>
|
||||
|
public RestApiResult SendRequest(string apiHost, |
||||
|
string apiPath, |
||||
|
object param, |
||||
|
IDictionary<string, string> requestHeaders, |
||||
|
HttpMethod httpMethod, |
||||
|
string contentType = ContentType_Json, |
||||
|
ParamPosition paramPosition = ParamPosition.Body, |
||||
|
bool enableRandomTimeStamp = false, |
||||
|
bool getResponseHeader = false, |
||||
|
HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, |
||||
|
string httpClientName = "", |
||||
|
int timeOutSeconds = 0) |
||||
|
{ |
||||
|
//Get和Delete强制使用QueryString形式传参
|
||||
|
if (httpMethod == HttpMethod.Get) |
||||
|
paramPosition = ParamPosition.Query; |
||||
|
|
||||
|
//拼接Url
|
||||
|
|
||||
|
var url = $"{apiHost}{(apiHost.EndsWith("/") ? string.Empty : (string.IsNullOrEmpty(apiPath) ? string.Empty : "/"))}{(apiPath.StartsWith("/") ? apiPath.Substring(1) : apiPath)}"; |
||||
|
|
||||
|
var isCombineParam = false; |
||||
|
if (paramPosition == ParamPosition.Query && param != null) |
||||
|
{ |
||||
|
url = $"{url}{(param.ToString().StartsWith("?") ? string.Empty : "?")}{param}"; |
||||
|
isCombineParam = true; |
||||
|
} |
||||
|
|
||||
|
//使用时间戳绕过CDN
|
||||
|
if (enableRandomTimeStamp) |
||||
|
url = $"{url}{(isCombineParam ? "&" : "?")}t={DateTime.Now.DateTimeToStamp()}"; |
||||
|
|
||||
|
using (var httpClient = string.IsNullOrEmpty(httpClientName) ? httpClientFactory.CreateClient() : httpClientFactory.CreateClient(httpClientName)) |
||||
|
{ |
||||
|
if (timeOutSeconds == 0) |
||||
|
httpClient.Timeout = TimeOut; |
||||
|
else |
||||
|
httpClient.Timeout = TimeSpan.FromSeconds(timeOutSeconds); |
||||
|
using (var request = new HttpRequestMessage(httpMethod, url)) |
||||
|
{ |
||||
|
if (requestHeaders != null && requestHeaders.Count > 0) |
||||
|
foreach (var key in requestHeaders.Keys) |
||||
|
request.Headers.Add(key, requestHeaders[key]); |
||||
|
|
||||
|
if (paramPosition == ParamPosition.Body && param != null) |
||||
|
request.Content = new StringContent(contentType == ContentType_Json ? JsonConvert.SerializeObject(param) : param.ToString(), Encoding.UTF8, contentType); |
||||
|
|
||||
|
using (var response = httpClient.SendAsync(request, httpCompletionOption).Result) |
||||
|
{ |
||||
|
return new RestApiResult() |
||||
|
{ |
||||
|
StatusCode = response.StatusCode, |
||||
|
Content = httpCompletionOption == HttpCompletionOption.ResponseContentRead ? response.Content.ReadAsStringAsync().Result : |
||||
|
string.Empty, |
||||
|
Headers = getResponseHeader ? response.Headers : null |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RestApiResult |
||||
|
{ |
||||
|
public HttpStatusCode StatusCode { get; set; } |
||||
|
|
||||
|
public string Content { get; set; } |
||||
|
|
||||
|
public HttpResponseHeaders Headers { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 参数传递位置
|
||||
|
/// </summary>
|
||||
|
public enum ParamPosition |
||||
|
{ |
||||
|
Query, |
||||
|
Body |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SiNan.Common.Models |
||||
|
{ |
||||
|
public class ApiResponse<T> |
||||
|
{ |
||||
|
public bool Success { get { return Code == 200; } } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 接口调用状态
|
||||
|
/// </summary>
|
||||
|
public int Code { get; set; } = 200; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 返回消息
|
||||
|
/// </summary>
|
||||
|
public string Msg { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 数据内容
|
||||
|
/// </summary>
|
||||
|
public T Data { get; set; } |
||||
|
|
||||
|
public static ApiResponse<T> Error(int code, string msg) |
||||
|
{ |
||||
|
return new ApiResponse<T>() { Code = code, Msg = msg }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ApiResponse : ApiResponse<object> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
namespace SiNan.Common.Models |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 业务异常
|
||||
|
/// </summary>
|
||||
|
public class BusinessException : Exception |
||||
|
{ |
||||
|
public BusinessException(string message) : base(message) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误代码
|
||||
|
/// </summary>
|
||||
|
public int Code { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace SiNan.Common.Models |
||||
|
{ |
||||
|
public interface IDenpendency |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="AutoMapper" Version="12.0.1" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> |
||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |
||||
|
<PackageReference Include="NLog" Version="5.2.5" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue