Browse Source

项目完善

master
shanji 3 years ago
parent
commit
d2b257ed36
  1. 7
      .editorconfig
  2. 15
      Binance.TradeRobot.API.sln
  3. 7
      Binance.TradeRobot.API/Binance.TradeRobot.API.csproj
  4. 86
      Binance.TradeRobot.API/Binance.TradeRobot.API.xml
  5. 11
      Binance.TradeRobot.API/Controllers/BaseApiController.cs
  6. 79
      Binance.TradeRobot.API/Controllers/UserController.cs
  7. 39
      Binance.TradeRobot.API/Controllers/WeatherForecastController.cs
  8. 45
      Binance.TradeRobot.API/Extensions/StartupExtenions.cs
  9. 4
      Binance.TradeRobot.API/Properties/launchSettings.json
  10. 59
      Binance.TradeRobot.API/Startup.cs
  11. 15
      Binance.TradeRobot.API/WeatherForecast.cs
  12. 7
      Binance.TradeRobot.API/appsettings.json
  13. 13
      Binance.TradeRobot.Business/BaseBusiness.cs
  14. 3
      Binance.TradeRobot.Business/Binance.TradeRobot.Business.csproj
  15. 8
      Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml
  16. 57
      Binance.TradeRobot.Business/UserBusiness.cs
  17. 15
      Binance.TradeRobot.Common/Binance.TradeRobot.Common.csproj
  18. 31
      Binance.TradeRobot.Common/DI/BatchRegistrationAttribute.cs
  19. 48
      Binance.TradeRobot.Common/Extensions/CryptographyExtension.cs
  20. 101
      Binance.TradeRobot.Common/Extensions/DateTimeExtension.cs
  21. 131
      Binance.TradeRobot.Common/Extensions/EnumExtension.cs
  22. 59
      Binance.TradeRobot.Common/Extensions/MapperExtension.cs
  23. 51
      Binance.TradeRobot.Common/Extensions/MathExtension.cs
  24. 27
      Binance.TradeRobot.Common/Helpers/ClearMemoryHelper.cs
  25. 49
      Binance.TradeRobot.Common/Helpers/ShellExecuteHelper.cs
  26. 456
      Binance.TradeRobot.Common/Http/HttpDownloader.cs
  27. 93
      Binance.TradeRobot.Common/Http/RestAPIService.cs
  28. 68
      Binance.TradeRobot.Common/Tasks/DelayTrigger.cs
  29. 156
      Binance.TradeRobot.Common/Tasks/LimitedConcurrencyLevelTaskScheduler.cs
  30. 8
      Binance.TradeRobot.Model/Base/MappingProfiles.cs
  31. 7
      Binance.TradeRobot.Model/Binance.TradeRobot.Model.csproj
  32. 123
      Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml
  33. 16
      Binance.TradeRobot.Model/Dto/Request/User/LoginRequest.cs
  34. 11
      Binance.TradeRobot.Model/Dto/Response/User/LoginResponse.cs
  35. 6
      Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs

7
.editorconfig

@ -0,0 +1,7 @@
[*.cs]
# CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
dotnet_diagnostic.CS8618.severity = none
# CS1591: 缺少对公共可见类型或成员的 XML 注释
dotnet_diagnostic.CS1591.severity = none

15
Binance.TradeRobot.API.sln

@ -5,9 +5,16 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binance.TradeRobot.API", "Binance.TradeRobot.API\Binance.TradeRobot.API.csproj", "{D568610C-F70C-406F-AB41-C6E2B89B327F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Binance.TradeRobot.Model", "Binance.TradeRobot.Model\Binance.TradeRobot.Model.csproj", "{2E56BEBE-2330-41D8-AAB7-B6B5CC707BBB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binance.TradeRobot.Model", "Binance.TradeRobot.Model\Binance.TradeRobot.Model.csproj", "{2E56BEBE-2330-41D8-AAB7-B6B5CC707BBB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Binance.TradeRobot.Business", "Binance.TradeRobot.Business\Binance.TradeRobot.Business.csproj", "{74CD58F4-1AA3-4EE9-A68B-386C76CF2125}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binance.TradeRobot.Business", "Binance.TradeRobot.Business\Binance.TradeRobot.Business.csproj", "{74CD58F4-1AA3-4EE9-A68B-386C76CF2125}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Binance.TradeRobot.Common", "Binance.TradeRobot.Common\Binance.TradeRobot.Common.csproj", "{C840DCF2-0E1E-4F9F-9EAA-5A4F80B51BD2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B55091A9-5FBF-486C-98D6-B7E4AF95D345}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -27,6 +34,10 @@ Global
{74CD58F4-1AA3-4EE9-A68B-386C76CF2125}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74CD58F4-1AA3-4EE9-A68B-386C76CF2125}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74CD58F4-1AA3-4EE9-A68B-386C76CF2125}.Release|Any CPU.Build.0 = Release|Any CPU
{C840DCF2-0E1E-4F9F-9EAA-5A4F80B51BD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C840DCF2-0E1E-4F9F-9EAA-5A4F80B51BD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C840DCF2-0E1E-4F9F-9EAA-5A4F80B51BD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C840DCF2-0E1E-4F9F-9EAA-5A4F80B51BD2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

7
Binance.TradeRobot.API/Binance.TradeRobot.API.csproj

@ -3,13 +3,19 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>Binance.TradeRobot.API.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FreeSql" Version="3.0.100" />
<PackageReference Include="FreeSql.Provider.SqlServer" Version="3.0.100" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.22" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.22" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.22" />
<PackageReference Include="NLog" Version="4.7.13" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.12" />
@ -17,6 +23,7 @@
<ItemGroup>
<ProjectReference Include="..\Binance.TradeRobot.Business\Binance.TradeRobot.Business.csproj" />
<ProjectReference Include="..\Binance.TradeRobot.Common\Binance.TradeRobot.Common.csproj" />
<ProjectReference Include="..\Binance.TradeRobot.Model\Binance.TradeRobot.Model.csproj" />
</ItemGroup>

86
Binance.TradeRobot.API/Binance.TradeRobot.API.xml

@ -0,0 +1,86 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Binance.TradeRobot.API</name>
</assembly>
<members>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.Login(Binance.TradeRobot.Model.Dto.LoginRequest)">
<summary>
用户登录
</summary>
<param name="loginRequest"></param>
<returns></returns>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.GetUserList">
<summary>
获取用户列表
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.GetUserAccountFundChangeRecordList">
<summary>
获取用户资金变更记录
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.GetUserAccountProfitLossRecordList">
<summary>
获取用户盈亏记录
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.AddFunds">
<summary>
追投
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.ReduceFunds">
<summary>
提现
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Controllers.UserController.TransferFunds">
<summary>
转移资金
</summary>
</member>
<member name="F:Binance.TradeRobot.API.Middlewares.CustomExceptionMiddleWare._next">
<summary>
管道请求委托
</summary>
</member>
<member name="F:Binance.TradeRobot.API.Middlewares.CustomExceptionMiddleWare._exceptionStatusCodeDic">
<summary>
需要处理的状态码字典
</summary>
</member>
<member name="M:Binance.TradeRobot.API.Middlewares.CustomExceptionMiddleWare.#ctor(Microsoft.AspNetCore.Http.RequestDelegate,Binance.TradeRobot.Business.NLogManager)">
<summary>
</summary>
<param name="next"></param>
<param name="nLogManager"></param>
</member>
<member name="M:Binance.TradeRobot.API.Middlewares.CustomExceptionMiddleWare.ErrorHandle(Microsoft.AspNetCore.Http.HttpContext,System.Int32,System.String)">
<summary>
处理方式:返回Json格式
</summary>
<param name="context"></param>
<param name="code"></param>
<param name="exMsg"></param>
<returns></returns>
</member>
<member name="M:Binance.TradeRobot.API.Extensions.StartupExtenions.UseCustomException(Microsoft.AspNetCore.Builder.IApplicationBuilder)">
<summary>
注册自定义异常中间件
</summary>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:Binance.TradeRobot.API.Extensions.StartupExtenions.AddSwagger(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String)">
<summary>
添加Swagger服务
</summary>
<param name="services"></param>
<param name="title"></param>
<returns></returns>
</member>
</members>
</doc>

11
Binance.TradeRobot.API/Controllers/BaseApiController.cs

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc;
namespace Binance.TradeRobot.API.Controllers
{
[Produces("application/json")]
[Route("Api/[Controller]/[Action]")]
[ApiController]
public class BaseApiController : ControllerBase
{
}
}

79
Binance.TradeRobot.API/Controllers/UserController.cs

@ -0,0 +1,79 @@
using Binance.TradeRobot.Business;
using Binance.TradeRobot.Model.Dto;
using Microsoft.AspNetCore.Mvc;
namespace Binance.TradeRobot.API.Controllers
{
public class UserController : BaseApiController
{
private UserBusiness userBusiness;
public UserController(UserBusiness userBusiness)
{
this.userBusiness = userBusiness;
}
/// <summary>
/// 用户登录
/// </summary>
/// <param name="loginRequest"></param>
/// <returns></returns>
[HttpPost]
public LoginResponse Login([FromBody] LoginRequest loginRequest)
{
return userBusiness.Login(loginRequest);
}
/// <summary>
/// 获取用户列表
/// </summary>
[HttpGet]
public void GetUserList()
{
}
/// <summary>
/// 获取用户资金变更记录
/// </summary>
[HttpGet]
public void GetUserAccountFundChangeRecordList()
{
}
/// <summary>
/// 获取用户盈亏记录
/// </summary>
[HttpGet]
public void GetUserAccountProfitLossRecordList()
{
}
/// <summary>
/// 追投
/// </summary>
[HttpPost]
public void AddFunds()
{
}
/// <summary>
/// 提现
/// </summary>
[HttpPost]
public void ReduceFunds()
{
}
/// <summary>
/// 转移资金
/// </summary>
[HttpPost]
public void TransferFunds()
{
}
}
}

39
Binance.TradeRobot.API/Controllers/WeatherForecastController.cs

@ -1,39 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Binance.TradeRobot.API.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}

45
Binance.TradeRobot.API/Extensions/StartupExtenions.cs

@ -1,4 +1,5 @@
using Binance.TradeRobot.API.Middlewares;
using Binance.TradeRobot.Common.DI;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
@ -13,41 +14,33 @@ namespace Binance.TradeRobot.API.Extensions
{
public static class StartupExtenions
{
/// <summary>
/// 批量注册服务
/// </summary>
/// <param name="services">DI服务</param>
/// <param name="assemblys">需要批量注册的程序集集合</param>
/// <param name="baseType">基础类/接口</param>
/// <param name="serviceLifetime">服务生命周期</param>
/// <returns></returns>
public static IServiceCollection BatchRegisterService(this IServiceCollection services, Assembly[] assemblys, Type baseType, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
public static IServiceCollection BatchRegisterService(this IServiceCollection services, Assembly[] assemblys)
{
List<Type> typeList = new List<Type>(); //所有符合注册条件的类集合
var registrationInterfaceTypes = new Dictionary<Type, Type[]>(); //待注册集合
var registrationSelfTypes = new List<Type>();
foreach (var assembly in assemblys)
{
//筛选当前程序集下符合条件的类
var types = assembly.GetTypes().Where(t => !t.IsInterface && !t.IsSealed && !t.IsAbstract && baseType.IsAssignableFrom(t));
if (types != null && types.Count() > 0)
typeList.AddRange(types);
}
if (typeList.Count() == 0)
return services;
var types = assembly.GetTypes().Where(t => !t.IsInterface && !t.IsSealed && !t.IsAbstract && Attribute.IsDefined(t, typeof(BatchRegistrationAttribute)));
if (types == null && types.Count() == 0)
continue;
foreach (var type in types)
{
var batchRegistrationAttribute = type.GetCustomAttribute<BatchRegistrationAttribute>(true);
if (batchRegistrationAttribute == null)
continue;
var typeDic = new Dictionary<Type, Type[]>(); //待注册集合
foreach (var type in typeList)
if (batchRegistrationAttribute.RegistrationType == RegistrationType.Self)
services.Add(new ServiceDescriptor(type, type, batchRegistrationAttribute.ServiceLifetime));
else if (batchRegistrationAttribute.RegistrationType == RegistrationType.Interface)
{
var interfaces = type.GetInterfaces(); //获取接口
typeDic.Add(type, interfaces);
}
if (typeDic.Keys.Count() > 0)
{
foreach (var instanceType in typeDic.Keys)
{
foreach (var interfaceType in typeDic[instanceType])
foreach (var interfaceType in interfaces)
{
//根据指定的生命周期进行注册
services.Add(new ServiceDescriptor(interfaceType, instanceType, serviceLifetime));
services.Add(new ServiceDescriptor(interfaceType, type, batchRegistrationAttribute.ServiceLifetime));
}
}
}
}

4
Binance.TradeRobot.API/Properties/launchSettings.json

@ -12,7 +12,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@ -20,7 +20,7 @@
"Binance.TradeRobot.API": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"launchUrl": "",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"

59
Binance.TradeRobot.API/Startup.cs

@ -1,10 +1,21 @@
using Binance.TradeRobot.API.Extensions;
using Binance.TradeRobot.API.Filters;
using Binance.TradeRobot.Business;
using Binance.TradeRobot.Common.Extensions;
using Binance.TradeRobot.Common.Http;
using Binance.TradeRobot.Model.Base;
using HY.TradingRobot.Model.Base;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;
using System.Text;
using Yitter.IdGenerator;
namespace Binance.TradeRobot.API
@ -21,6 +32,9 @@ namespace Binance.TradeRobot.API
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<NLogManager>();
services.AddSingleton<RestApiService>();
var fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.SqlServer, Configuration.GetConnectionString("DB")).Build();
services.AddSingleton(typeof(IFreeSql), fsql);
@ -47,26 +61,61 @@ namespace Binance.TradeRobot.API
setupAction.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
setupAction.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Include;
});
services.AddSwagger("±Ò°²×Ô¶¯½»Ò×API");
services.BatchRegisterService(new Assembly[] { Assembly.Load("Binance.TradeRobot.Business") });
services.AddMapper(new MappingProfiles());
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver(),
DateFormatString = "yyyy-MM-dd HH:mm:ss",
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, x =>
{
var jwtSecret = Configuration.GetSection("JwtConfig:Secret").Value;
var jwtIssuer = Configuration.GetSection("JwtConfig:Issuer").Value;
var jwtAudience = Configuration.GetSection("JwtConfig:Audience").Value;
x.SaveToken = true;
x.RequireHttpsMetadata = false;
x.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)),
ValidIssuer = jwtIssuer,
ValidAudience = jwtAudience,
ValidateLifetime = true
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseSwagger(c => c.SerializeAsV2 = true)
.UseSwaggerUI(c =>
{
app.UseDeveloperExceptionPage();
}
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Binance.TradeRobot.API");
c.RoutePrefix = string.Empty;
});
app.UseHttpsRedirection();
app.UseCustomException();
app.UseRouting();
//app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//websocket
}
}
}

15
Binance.TradeRobot.API/WeatherForecast.cs

@ -1,15 +0,0 @@
using System;
namespace Binance.TradeRobot.API
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

7
Binance.TradeRobot.API/appsettings.json

@ -9,6 +9,11 @@
"AllowedHosts": "*",
"ConnectionStrings": {
//"DB": "Data Source=192.168.201.44;Initial Catalog=HY.TradingRobot.DB;User ID=sa;Pwd=kaicn1132+-;"
"DB": "Data Source=.;Initial Catalog=Binance.TradeRobot.DB;User ID=sa;Pwd=pc911103;"
"DB": "Data Source=.;Initial Catalog=Binance.TradeRobot.DB;User ID=sa;Pwd=pc911103;Encrypt=True; TrustServerCertificate=True;"
},
"JwtConfig": {
"Secret": "heyitraderobot11",
"Issuer": "heyi",
"Audience": "heyi"
}
}

13
Binance.TradeRobot.Business/BaseBusiness.cs

@ -0,0 +1,13 @@
namespace Binance.TradeRobot.Business
{
public class BaseBusiness
{
protected IFreeSql fsql;
protected NLogManager logManager;
public BaseBusiness(IFreeSql fsql, NLogManager logManager)
{
this.fsql = fsql;
this.logManager = logManager;
}
}
}

3
Binance.TradeRobot.Business/Binance.TradeRobot.Business.csproj

@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>Binance.TradeRobot.Business.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@ -13,6 +15,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Binance.TradeRobot.Common\Binance.TradeRobot.Common.csproj" />
<ProjectReference Include="..\Binance.TradeRobot.Model\Binance.TradeRobot.Model.csproj" />
</ItemGroup>

8
Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Binance.TradeRobot.Business</name>
</assembly>
<members>
</members>
</doc>

57
Binance.TradeRobot.Business/UserBusiness.cs

@ -0,0 +1,57 @@
using Binance.TradeRobot.Common.DI;
using Binance.TradeRobot.Common.Extensions;
using Binance.TradeRobot.Model.Base;
using Binance.TradeRobot.Model.Db;
using Binance.TradeRobot.Model.Dto;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Binance.TradeRobot.Business
{
[BatchRegistration(ServiceLifetime.Singleton, RegistrationType.Self)]
public class UserBusiness : BaseBusiness
{
private IConfiguration configuration;
public UserBusiness(IFreeSql fsql, NLogManager logManager, IConfiguration configuration) : base(fsql, logManager)
{
this.configuration = configuration;
}
public LoginResponse Login(LoginRequest loginRequest)
{
var pwd = loginRequest.Pwd.ToMD5();
var user = fsql.Select<User>().Where(u => u.UserName == loginRequest.UserName && u.Pwd == pwd).ToOne();
if (user == null)
throw new BusinessException("用户名或密码错误");
#region 签发token
var claims = new List<Claim>()
{
new Claim("Id",user.Id.ToString()),
new Claim("UserName",user.UserName),
};
var jwtSecret = configuration.GetSection("JwtConfig:Secret").Value;
var jwtIssuer = configuration.GetSection("JwtConfig:Issuer").Value;
var jwtAudience = configuration.GetSection("JwtConfig:Audience").Value;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken(jwtIssuer, jwtAudience, claims, expires: DateTime.Now.AddDays(30), signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
#endregion
return new LoginResponse()
{
Id = user.Id,
UserName = user.UserName,
Token = token
};
}
}
}

15
Binance.TradeRobot.Common/Binance.TradeRobot.Common.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.22" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.22" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

31
Binance.TradeRobot.Common/DI/BatchRegistrationAttribute.cs

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Binance.TradeRobot.Common.DI
{
[AttributeUsage(AttributeTargets.Class)]
public class BatchRegistrationAttribute : Attribute
{
public ServiceLifetime ServiceLifetime;
public RegistrationType RegistrationType;
/// <summary>
/// 批量注册特性
/// </summary>
/// <param name="serviceLifetime">生命周期</param>
/// <param name="registrationType">注册类型</param>
public BatchRegistrationAttribute(ServiceLifetime serviceLifetime, RegistrationType registrationType)
{
this.ServiceLifetime = serviceLifetime;
this.RegistrationType = registrationType;
}
}
/// <summary>
/// 注册类型
/// </summary>
public enum RegistrationType
{
Interface, Self
}
}

48
Binance.TradeRobot.Common/Extensions/CryptographyExtension.cs

@ -0,0 +1,48 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace Binance.TradeRobot.Common.Extensions
{
public static class CryptographyExtension
{
public static string ToHmacSha256(this string str, string secret, bool toBase64 = true)
{
secret = secret ?? "";
byte[] keyByte = Encoding.UTF8.GetBytes(secret);
byte[] strBytes = Encoding.UTF8.GetBytes(str);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashStr = hmacsha256.ComputeHash(strBytes);
if (toBase64)
return Convert.ToBase64String(hashStr);
else
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hashStr.Length; i++)
{
builder.Append(hashStr[i].ToString("x2"));
}
return builder.ToString();
}
}
}
public static string ToMD5(this string str, bool isUpper = false)
{
var format = isUpper ? "X2" : "x2";
//32位大写
using (var md5 = MD5.Create())
{
var data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
StringBuilder builder = new StringBuilder();
// 循环遍历哈希数据的每一个字节并格式化为十六进制字符串
for (int i = 0; i < data.Length; i++)
{
builder.Append(data[i].ToString(format));
}
return builder.ToString();
}
}
}
}

101
Binance.TradeRobot.Common/Extensions/DateTimeExtension.cs

@ -0,0 +1,101 @@
using System;
using System.Runtime.InteropServices;
namespace Binance.TradeRobot.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>
[Obsolete]
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
}
}

131
Binance.TradeRobot.Common/Extensions/EnumExtension.cs

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace Binance.TradeRobot.Common.Extensions
{
public static class EnumExtension
{
/// <summary>
/// 从枚举中获取Description
/// </summary>
/// <param name="enumName">需要获取枚举描述的枚举</param>
/// <returns>描述内容</returns>
public static string GetDescription(this System.Enum enumName)
{
var fieldInfo = enumName.GetType().GetField(enumName.ToString());
var attributes = GetDescriptAttr(fieldInfo);
string description;
if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
else
{
description = enumName.ToString();
}
return description;
}
/// <summary>
/// 根据 value 值获取Description
/// </summary>
/// <param name="enumType"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetDescription(this Type enumType, int value)
{
var Key = GetNameAndValue(enumType).FirstOrDefault(p => p.Value.Equals(value)).Key;
if (Key == null)
return null;
return Key.ToString();
}
/// <summary>
/// 获取字段Description
/// </summary>
/// <param name="fieldInfo">FieldInfo</param>
/// <returns>DescriptionAttribute[] </returns>
private static DescriptionAttribute[] GetDescriptAttr(FieldInfo fieldInfo)
{
if (fieldInfo != null)
{
return (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
}
return null;
}
/// <summary>
/// 获取枚举所有名称
/// </summary>
/// <param name="enumType">枚举类型typeof(T)</param>
/// <returns>枚举名称列表</returns>
public static List<string> GetEnumNamesList(this Type enumType)
{
return Enum.GetNames(enumType).ToList();
}
/// <summary>
/// 获取所有枚举对应的值
/// </summary>
/// <param name="enumType">枚举类型typeof(T)</param>
/// <returns>枚举值列表</returns>
public static List<int> GetEnumValuesList(this Type enumType)
{
var list = new List<int>();
foreach (var value in System.Enum.GetValues(enumType))
{
list.Add(Convert.ToInt32(value));
}
return list;
}
/// <summary>
/// 获取枚举名以及对应的Description
/// </summary>
/// <param name="type">枚举类型typeof(T)</param>
/// <returns>返回Dictionary ,Key为枚举名, Value为枚举对应的Description</returns>
public static Dictionary<object, object> GetNameAndDescriptions(this Type type)
{
if (type.IsEnum)
{
var dic = new Dictionary<object, object>();
var enumValues = System.Enum.GetValues(type);
foreach (System.Enum value in enumValues)
{
dic.Add(value, GetDescription(value));
}
return dic;
}
return null;
}
/// <summary>
/// 获取枚举名以及对应的Value
/// </summary>
/// <param name="type">枚举类型typeof(T)</param>
/// <returns>返回Dictionary ,Key为描述名, Value为枚举对应的值</returns>
public static Dictionary<object, object> GetNameAndValue(this Type type)
{
if (type.IsEnum)
{
var dic = new Dictionary<object, object>();
var enumValues = System.Enum.GetValues(type);
foreach (System.Enum value in enumValues)
{
dic.Add(GetDescription(value), value.GetHashCode());
}
return dic;
}
return null;
}
public static int GetValue(this System.Enum enumName)
{
return Convert.ToInt32(enumName.GetType().GetField(enumName.ToString()).GetValue(enumName));
}
}
}

59
Binance.TradeRobot.Common/Extensions/MapperExtension.cs

@ -0,0 +1,59 @@
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
namespace Binance.TradeRobot.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);
}
}
}

51
Binance.TradeRobot.Common/Extensions/MathExtension.cs

@ -0,0 +1,51 @@
using System;
namespace Binance.TradeRobot.Common.Extensions
{
public static class MathExtension
{
/// <summary>
/// 四舍五入保留小数
/// </summary>
/// <param name="d"></param>
/// <param name="length">保留的位数</param>
/// <returns></returns>
public static decimal Round(this decimal d, int length)
{
return Math.Round(d, length, MidpointRounding.AwayFromZero);
}
/// <summary>
/// string转换decimal
/// </summary>
/// <param name="s"></param>
/// <param name="length"></param>
/// <returns></returns>
public static decimal ToDecimal(this string s, int? length = null)
{
if (!decimal.TryParse(s, out decimal d))
return 0M;
if (length != null)
return d.Round(length.Value);
return d;
}
/// <summary>
/// 截取小数位数,不进行四舍五入
/// </summary>
/// <param name="d"></param>
/// <param name="length">截取的位数</param>
/// <returns></returns>
public static decimal CutDecimal(this decimal d, int length)
{
var dStr = d.ToString();
var dotIndex = dStr.IndexOf(".");
var cutLength = dotIndex + 1 + length;
if (dotIndex == -1 || dStr.Length <= cutLength)
dStr = string.Format($"{{0:F{length}}}", d);
else
dStr = dStr.Substring(0, cutLength);
return decimal.Parse(dStr);
}
}
}

27
Binance.TradeRobot.Common/Helpers/ClearMemoryHelper.cs

@ -0,0 +1,27 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Binance.TradeRobot.Common.Helpers
{
/// <summary>
/// 清理内存
/// </summary>
public class ClearMemoryHelper
{
[DllImport("psapi.dll")]
static extern int EmptyWorkingSet(IntPtr hwProc);
public static void ClearMemory(string processName)
{
Process[] processes = string.IsNullOrEmpty(processName) ?
new Process[] { Process.GetCurrentProcess() } :
Process.GetProcessesByName(processName);
try
{
Array.ForEach(processes, p => EmptyWorkingSet(p.Handle));
}
catch { throw; }
}
}
}

49
Binance.TradeRobot.Common/Helpers/ShellExecuteHelper.cs

@ -0,0 +1,49 @@
using System;
using System.Runtime.InteropServices;
namespace Binance.TradeRobot.Common.Helpers
{
public class ShellExecuteHelper
{
public enum ShowCommands : int
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
/// <summary>
/// 内核调用
/// <para>
/// ShellExecuteHelper.ShellExecute(IntPtr.Zero, "open", <anyPath>, string.Empty, string.Empty, ShellExecuteHelper.ShowCommands.SW_SHOWNORMAL);
/// </para>
/// </summary>
/// <param name="hwnd"></param>
/// <param name="lpOperation"></param>
/// <param name="lpFile"></param>
/// <param name="lpParameters"></param>
/// <param name="lpDirectory"></param>
/// <param name="nShowCmd"></param>
/// <returns></returns>
[DllImport("shell32.dll")]
public static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);
}
}

456
Binance.TradeRobot.Common/Http/HttpDownloader.cs

@ -0,0 +1,456 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
namespace Binance.TradeRobot.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;
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;
}
}

93
Binance.TradeRobot.Common/Http/RestAPIService.cs

@ -0,0 +1,93 @@
using Binance.TradeRobot.Common.Extensions;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
namespace Binance.TradeRobot.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, 20);
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="headers"></param>
/// <param name="httpMethod"></param>
/// <param name="contentType"></param>
/// <param name="paramPosition"></param>
/// <param name="enableRandomTimeStamp"></param>
/// <returns></returns>
public string SendRequest(string apiHost,
string apiPath,
object param,
IDictionary<string, string> headers,
HttpMethod httpMethod,
string contentType = ContentType_Json,
ParamPosition paramPosition = ParamPosition.Body,
bool enableRandomTimeStamp = false)
{
//Get和Delete强制使用QueryString形式传参
if (httpMethod == HttpMethod.Get || httpMethod == HttpMethod.Delete)
paramPosition = ParamPosition.Query;
//拼接Url
var url = $"{apiHost}{(apiHost.EndsWith("/") ? 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 = httpClientFactory.CreateClient())
{
httpClient.Timeout = TimeOut;
using (var request = new HttpRequestMessage(httpMethod, url))
{
if (headers != null && headers.Count > 0)
foreach (var key in headers.Keys)
request.Headers.Add(key, headers[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).Result)
{
if (!response.IsSuccessStatusCode)
throw new Exception($"HttpCode {response.StatusCode}");
return response.Content.ReadAsStringAsync().Result;
}
}
}
}
}
/// <summary>
/// 参数传递位置
/// </summary>
public enum ParamPosition
{
Query,
Body
}
}

68
Binance.TradeRobot.Common/Tasks/DelayTrigger.cs

@ -0,0 +1,68 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Binance.TradeRobot.Common
{
/// <summary>
/// 延迟触发器
/// </summary>
public class DelayTrigger
{
public DelayTrigger(int delayTime = 500)
{
if (delayTime < 500)
delayTime = 500;
this.delayTime = delayTime;
}
/// <summary>
/// 延迟执行时间(ms)
/// </summary>
private int delayTime;
/// <summary>
/// 关键字
/// </summary>
private string currentKey;
/// <summary>
/// 是否可以执行
/// </summary>
private volatile bool canExecute;
/// <summary>
/// 是否正在延迟响应中
/// </summary>
private volatile bool isDelaying;
public Action<string> OnExecute;
public void SetKey(string key)
{
currentKey = key;
if (isDelaying)
{
canExecute = false;
return;
}
Task.Factory.StartNew(delegate
{
isDelaying = true;
while (true)
{
canExecute = true;
Thread.Sleep(delayTime);
if (canExecute)
{
Console.WriteLine($"{currentKey} Execute at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")}");
OnExecute?.Invoke(currentKey);
isDelaying = false;
break;
}
}
});
}
}
}

156
Binance.TradeRobot.Common/Tasks/LimitedConcurrencyLevelTaskScheduler.cs

@ -0,0 +1,156 @@
//--------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: LimitedConcurrencyTaskScheduler.cs
//
//--------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Binance.TradeRobot.Common.Tasks
{
/// <summary>
/// Provides a task scheduler that ensures a maximum concurrency level while
/// running on top of the ThreadPool.
/// </summary>
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
/// <summary>Whether the current thread is processing work items.</summary>
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
/// <summary>The list of tasks to be executed.</summary>
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
private readonly int _maxDegreeOfParallelism;
/// <summary>Whether the scheduler is currently processing work items.</summary>
private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)
/// <summary>
/// 最大并发数
/// </summary>
private readonly int maxConcurrencyCountOfSystem = Environment.ProcessorCount * 2;
/// <summary>
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
/// specified degree of parallelism.
/// </summary>
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
if (maxDegreeOfParallelism > maxConcurrencyCountOfSystem)
maxDegreeOfParallelism = maxConcurrencyCountOfSystem;
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
/// <summary>Queues a task to the scheduler.</summary>
/// <param name="task">The task to be queued.</param>
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
++_delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
}
/// <summary>
/// Informs the ThreadPool that there's work to be executed for this scheduler.
/// </summary>
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
//new Thread(() =>
//{
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
--_delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
base.TryExecuteTask(item);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
//})
//{ IsBackground = true }.Start();
}, null);
}
/// <summary>Attempts to execute the specified task on the current thread.</summary>
/// <param name="task">The task to be executed.</param>
/// <param name="taskWasPreviouslyQueued"></param>
/// <returns>Whether the task could be executed on the current thread.</returns>
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued) TryDequeue(task);
// Try to run the task.
return base.TryExecuteTask(task);
}
/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
/// <param name="task">The task to be removed.</param>
/// <returns>Whether the task could be found and removed.</returns>
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
/// <returns>An enumerable of the tasks currently scheduled.</returns>
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks.ToArray();
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
}

8
Binance.TradeRobot.Model/Base/MappingProfiles.cs

@ -0,0 +1,8 @@
using AutoMapper;
namespace Binance.TradeRobot.Model.Base
{
public class MappingProfiles : Profile
{
}
}

7
Binance.TradeRobot.Model/Binance.TradeRobot.Model.csproj

@ -3,13 +3,12 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>Binance.TradeRobot.Model.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Folder Include="Dto\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="FreeSql" Version="3.0.100" />
</ItemGroup>

123
Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml

@ -0,0 +1,123 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Binance.TradeRobot.Model</name>
</assembly>
<members>
<member name="T:Binance.TradeRobot.Model.Base.BusinessException">
<summary>
业务异常
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Base.BusinessException.Code">
<summary>
错误代码
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.BusinessType.Spot">
<summary>
现货
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.BusinessType.Prep">
<summary>
合约
</summary>
</member>
<member name="T:Binance.TradeRobot.Model.Base.Enums.CapitalChangeType">
<summary>
资金变更类型 追投=0,提现=1,转移=2
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.CapitalChangeType.Add">
<summary>
增加资金
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.CapitalChangeType.Reduce">
<summary>
减少资金
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.CapitalChangeType.Transfer">
<summary>
转移资金
</summary>
</member>
<member name="T:Binance.TradeRobot.Model.Base.Enums.FundDirection">
<summary>
资金方向 转入=0,转出=1
</summary>
</member>
<member name="T:Binance.TradeRobot.Model.Base.Enums.RobotStatus">
<summary>
机器人状态 Stop=0,Runing=1
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.User.CostAmount">
<summary>
投资本金
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.User.Profit">
<summary>
收益
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.User.WithdrawAmount">
<summary>
提前金额
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountFundChangeRecord.ChangeAmount">
<summary>
变更金额
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountFundChangeRecord.OperationUserId">
<summary>
操作者Id
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountFundChangeRecord.UserId">
<summary>
用户Id
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountFundChangeRecord.ToUserId">
<summary>
对端用户Id
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountProfitLossRecord.BusinessType">
<summary>
业务类型
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountProfitLossRecord.ChangeAmount">
<summary>
变更金额
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountProfitLossRecord.DividendRatio">
<summary>
分红比例
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountProfitLossRecord.OrderProfit">
<summary>
订单利润
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Db.UserAccountProfitLossRecord.UserProfit">
<summary>
用户投资收益
</summary>
</member>
<member name="P:Binance.TradeRobot.Model.Dto.LoginRequest.Pwd">
<summary>
密码需要Md5小写加密
</summary>
</member>
</members>
</doc>

16
Binance.TradeRobot.Model/Dto/Request/User/LoginRequest.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Binance.TradeRobot.Model.Dto
{
public class LoginRequest
{
public string UserName { get; set; }
/// <summary>
/// 密码需要Md5小写加密
/// </summary>
public string Pwd { get; set; }
}
}

11
Binance.TradeRobot.Model/Dto/Response/User/LoginResponse.cs

@ -0,0 +1,11 @@
namespace Binance.TradeRobot.Model.Dto
{
public class LoginResponse
{
public long Id { get; set; }
public string UserName { get; set; }
public string Token { get; set; }
}
}

6
Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs

@ -0,0 +1,6 @@
namespace Binance.TradeRobot.Model.Dto
{
public class UserResponse
{
}
}
Loading…
Cancel
Save