35 changed files with 1821 additions and 98 deletions
@ -0,0 +1,7 @@ |
|||||
|
[*.cs] |
||||
|
|
||||
|
# CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 |
||||
|
dotnet_diagnostic.CS8618.severity = none |
||||
|
|
||||
|
# CS1591: 缺少对公共可见类型或成员的 XML 注释 |
||||
|
dotnet_diagnostic.CS1591.severity = none |
@ -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> |
@ -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 |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -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() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -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; } |
|
||||
} |
|
||||
} |
|
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<doc> |
||||
|
<assembly> |
||||
|
<name>Binance.TradeRobot.Business</name> |
||||
|
</assembly> |
||||
|
<members> |
||||
|
</members> |
||||
|
</doc> |
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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> |
@ -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 |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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
|
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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 |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
using AutoMapper; |
||||
|
|
||||
|
namespace Binance.TradeRobot.Model.Base |
||||
|
{ |
||||
|
public class MappingProfiles : Profile |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -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> |
@ -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; } |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
namespace Binance.TradeRobot.Model.Dto |
||||
|
{ |
||||
|
public class UserResponse |
||||
|
{ |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue