16 changed files with 548 additions and 12 deletions
@ -0,0 +1,91 @@ |
|||
using Binance.TradeRobot.Business; |
|||
using Binance.TradeRobot.Model.Base; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Newtonsoft.Json; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Binance.TradeRobot.API.Middlewares |
|||
{ |
|||
public class CustomExceptionMiddleWare |
|||
{ |
|||
/// <summary>
|
|||
/// 管道请求委托
|
|||
/// </summary>
|
|||
private RequestDelegate _next; |
|||
|
|||
/// <summary>
|
|||
/// 需要处理的状态码字典
|
|||
/// </summary>
|
|||
private IDictionary<int, string> _exceptionStatusCodeDic; |
|||
|
|||
private NLogManager nLogManager; |
|||
|
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
/// <param name="next"></param>
|
|||
/// <param name="nLogManager"></param>
|
|||
public CustomExceptionMiddleWare(RequestDelegate next, NLogManager nLogManager) |
|||
{ |
|||
_next = next; |
|||
this.nLogManager = nLogManager; |
|||
_exceptionStatusCodeDic = new Dictionary<int, string> |
|||
{ |
|||
{ 401, "未授权的请求" }, |
|||
{ 404, "找不到该资源" }, |
|||
{ 403, "访问被拒绝" }, |
|||
{ 500, "服务器发生意外的错误" }, |
|||
{ 503, "服务不可用" } |
|||
//其余状态自行扩展
|
|||
}; |
|||
} |
|||
|
|||
public async Task Invoke(HttpContext context) |
|||
{ |
|||
try |
|||
{ |
|||
await _next(context); //调用管道执行下一个中间件
|
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (ex is BusinessException) |
|||
{ |
|||
var busEx = ex as BusinessException; |
|||
context.Response.StatusCode = 200; //业务异常时将Http状态码改为200
|
|||
await ErrorHandle(context, busEx.Code, busEx.Message); |
|||
} |
|||
else |
|||
{ |
|||
context.Response.Clear(); |
|||
context.Response.StatusCode = 500; //发生未捕获的异常,手动设置状态码
|
|||
nLogManager.Default().Error(ex); //记录错误
|
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
if (_exceptionStatusCodeDic.TryGetValue(context.Response.StatusCode, out string exMsg)) |
|||
{ |
|||
await ErrorHandle(context, context.Response.StatusCode, exMsg); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理方式:返回Json格式
|
|||
/// </summary>
|
|||
/// <param name="context"></param>
|
|||
/// <param name="code"></param>
|
|||
/// <param name="exMsg"></param>
|
|||
/// <returns></returns>
|
|||
private async Task ErrorHandle(HttpContext context, int code, string exMsg) |
|||
{ |
|||
var apiResponse = new ApiResponse() { Code = code, Message = exMsg }; |
|||
var serialzeStr = JsonConvert.SerializeObject(apiResponse); |
|||
context.Response.ContentType = "application/json"; |
|||
await context.Response.WriteAsync(serialzeStr, Encoding.UTF8); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,139 @@ |
|||
using Binance.TradeRobot.API.Middlewares; |
|||
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.OpenApi.Models; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
|
|||
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) |
|||
{ |
|||
List<Type> typeList = 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 typeDic = new Dictionary<Type, Type[]>(); //待注册集合
|
|||
foreach (var type in typeList) |
|||
{ |
|||
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]) |
|||
{ |
|||
//根据指定的生命周期进行注册
|
|||
services.Add(new ServiceDescriptor(interfaceType, instanceType, serviceLifetime)); |
|||
} |
|||
} |
|||
} |
|||
return services; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 注册自定义异常中间件
|
|||
/// </summary>
|
|||
/// <param name="app"></param>
|
|||
/// <returns></returns>
|
|||
public static IApplicationBuilder UseCustomException(this IApplicationBuilder app) |
|||
{ |
|||
return app.UseMiddleware<CustomExceptionMiddleWare>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加Swagger服务
|
|||
/// </summary>
|
|||
/// <param name="services"></param>
|
|||
/// <param name="title"></param>
|
|||
/// <returns></returns>
|
|||
public static IServiceCollection AddSwagger(this IServiceCollection services, string title) |
|||
{ |
|||
return services.AddSwaggerGen(c => |
|||
{ |
|||
c.SwaggerDoc("v1", new OpenApiInfo |
|||
{ |
|||
Version = "v1.0.0", |
|||
Title = title, |
|||
Description = "注意事项\r\n1.返回参数名称采用大驼峰命名\r\n2.ApiResponse为基础返回对象(Code,Data,Message),接口中所有的返回值均属于Data属性\r\n3.正常返回Code=200" |
|||
}); |
|||
// JWT认证
|
|||
c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme |
|||
{ |
|||
Scheme = JwtBearerDefaults.AuthenticationScheme, |
|||
BearerFormat = "JWT", |
|||
Type = SecuritySchemeType.ApiKey, |
|||
Name = "Authorization", |
|||
In = ParameterLocation.Header, |
|||
Description = "Authorization:Bearer {your JWT token}<br/>", |
|||
}); |
|||
c.AddSecurityRequirement(new OpenApiSecurityRequirement |
|||
{ |
|||
{ |
|||
new OpenApiSecurityScheme{Reference = new OpenApiReference |
|||
{ |
|||
Type = ReferenceType.SecurityScheme, |
|||
Id = JwtBearerDefaults.AuthenticationScheme |
|||
} |
|||
}, |
|||
new string[] { } |
|||
} |
|||
}); |
|||
|
|||
var executingAssembly = Assembly.GetExecutingAssembly(); |
|||
var assemblyNames = executingAssembly.GetReferencedAssemblies().Union(new AssemblyName[] { executingAssembly.GetName() }).ToArray(); |
|||
Array.ForEach(assemblyNames, (assemblyName) => |
|||
{ |
|||
//var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
|||
var xmlFile = $"{assemblyName.Name}.xml"; |
|||
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); |
|||
if (!File.Exists(xmlPath)) |
|||
return; |
|||
c.IncludeXmlComments(xmlPath); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
//public static IServiceCollection AddWebSocket(this IServiceCollection services)
|
|||
//{
|
|||
// services.AddSingleton<WebSocketConnectionManager>();
|
|||
// services.AddSingleton<WebSocketHandler, TradeRobotWebSocketHandler>();
|
|||
// return services;
|
|||
//}
|
|||
|
|||
/// <summary>
|
|||
/// 注册websocket中间件
|
|||
/// </summary>
|
|||
/// <param name="app"></param>
|
|||
/// <param name="handler"></param>
|
|||
/// <returns></returns>
|
|||
//public static IApplicationBuilder UseWebSocketMiddleware(this IApplicationBuilder app, WebSocketHandler handler)
|
|||
//{
|
|||
// // return app.Map(path, (_app) => _app.UseMiddleware<WebSocketManagerMiddleware>(handler));
|
|||
// return app.UseMiddleware<WebSocketManagerMiddleware>(handler);
|
|||
//}
|
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
using Binance.TradeRobot.Model.Base; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
|
|||
namespace Binance.TradeRobot.API.Filters |
|||
{ |
|||
public class ResultFilter : IResultFilter |
|||
{ |
|||
public void OnResultExecuted(ResultExecutedContext context) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public void OnResultExecuting(ResultExecutingContext context) |
|||
{ |
|||
if (context.Result is ObjectResult) |
|||
{ |
|||
var objectResult = context.Result as ObjectResult; |
|||
if (!(objectResult.Value is ApiResponse)) |
|||
{ |
|||
objectResult.Value = new ApiResponse() { Data = objectResult.Value }; |
|||
} |
|||
} |
|||
else if (context.Result is EmptyResult) |
|||
{ |
|||
context.Result = new ObjectResult(new ApiResponse()); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
|||
<targets> |
|||
<target name="errorFile" xsi:type="File" fileName="${basedir}/logs/${logger}/error/${shortdate}.txt" |
|||
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}${onexception:${exception:format=tostring} ${newline}${stacktrace} ${newline}${newline}" |
|||
autoFlush="true"/> |
|||
<target name="infoFile" xsi:type="File" fileName="${basedir}/logs/${logger}/info/${shortdate}.txt" |
|||
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}" |
|||
autoFlush="true"/> |
|||
</targets> |
|||
<rules> |
|||
<logger name="*" level="Error" writeTo="errorFile"/> |
|||
<logger name="*" level="Info" writeTo="infoFile" /> |
|||
</rules> |
|||
</nlog> |
@ -0,0 +1,36 @@ |
|||
using NLog; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Binance.TradeRobot.Business |
|||
{ |
|||
public class NLogManager |
|||
{ |
|||
private IDictionary<string, ILogger> loggerDictionary = new Dictionary<string, ILogger>(); |
|||
private string defaultLoggerName = "default"; |
|||
|
|||
public NLogManager() |
|||
{ |
|||
loggerDictionary = new Dictionary<string, ILogger>() |
|||
{ |
|||
{ "default",NLog.LogManager.GetLogger(defaultLoggerName)} |
|||
}; |
|||
} |
|||
|
|||
public ILogger Default() |
|||
{ |
|||
return loggerDictionary[defaultLoggerName]; |
|||
} |
|||
|
|||
public ILogger GetLogger(string loggerName) |
|||
{ |
|||
if (string.IsNullOrEmpty(loggerName)) |
|||
return Default(); |
|||
if (!loggerDictionary.TryGetValue(loggerName, out ILogger logger)) |
|||
{ |
|||
logger = NLog.LogManager.GetLogger(loggerName); |
|||
loggerDictionary.TryAdd(loggerName, logger); |
|||
} |
|||
return logger; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
namespace Binance.TradeRobot.Model.Base |
|||
{ |
|||
public class ApiResponse<T> |
|||
{ |
|||
public int Code { get; set; } = 200; |
|||
|
|||
public T Data { get; set; } |
|||
|
|||
public string Message { get; set; } |
|||
} |
|||
|
|||
public class ApiResponse : ApiResponse<object> |
|||
{ |
|||
|
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
using FreeSql.DataAnnotations; |
|||
using System; |
|||
|
|||
namespace Binance.TradeRobot.Model.Db |
|||
{ |
|||
|
|||
[Table(DisableSyncStructure = true)] |
|||
public partial class User { |
|||
|
|||
[Column(IsPrimary = true)] |
|||
public long Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 投资本金
|
|||
/// </summary>
|
|||
[ Column(DbType = "decimal(18,8)")] |
|||
public decimal CostAmount { get; set; } = 0.0M; |
|||
|
|||
[Column(InsertValueSql = "getdate()")] |
|||
public DateTime? CreateTime { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 收益
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal Profit { get; set; } = 0.0M; |
|||
|
|||
[Column(StringLength = 20)] |
|||
public string Pwd { get; set; } |
|||
|
|||
[Column(InsertValueSql = "getdate()")] |
|||
public DateTime? UpdateTime { get; set; } |
|||
|
|||
[Column(StringLength = 20)] |
|||
public string UserName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 提前金额
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal WithdrawAmount { get; set; } = 0.0M; |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,46 @@ |
|||
using Binance.TradeRobot.Model.Base; |
|||
using FreeSql.DataAnnotations; |
|||
using System; |
|||
|
|||
namespace Binance.TradeRobot.Model.Db |
|||
{ |
|||
|
|||
[Table(DisableSyncStructure = true)] |
|||
public partial class UserAccountFundChangeRecord |
|||
{ |
|||
|
|||
[Column(IsPrimary = true)] |
|||
public long Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 变更金额
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal ChangeAmount { get; set; } |
|||
|
|||
[Column(InsertValueSql = "getdate()")] |
|||
public DateTime? CreateTime { get; set; } |
|||
|
|||
[Column(MapType = typeof(int))] |
|||
public Enums.FundDirection Direction { get; set; } |
|||
|
|||
[Column(MapType = typeof(int))] |
|||
public Enums.CapitalChangeType OperationType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 操作者Id
|
|||
/// </summary>
|
|||
public long OperationUserId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 用户Id
|
|||
/// </summary>
|
|||
public long UserId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 对端用户Id
|
|||
/// </summary>
|
|||
public long? ToUserId { get; set; } |
|||
} |
|||
|
|||
} |
@ -0,0 +1,59 @@ |
|||
using Binance.TradeRobot.Model.Base; |
|||
using FreeSql.DataAnnotations; |
|||
using System; |
|||
|
|||
namespace Binance.TradeRobot.Model.Db |
|||
{ |
|||
|
|||
[Table(DisableSyncStructure = true)] |
|||
public partial class UserAccountProfitLossRecord |
|||
{ |
|||
|
|||
/// <summary>
|
|||
/// 业务类型
|
|||
/// </summary>
|
|||
[Column(MapType = typeof(int))] |
|||
public Enums.BusinessType BusinessType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 变更金额
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal ChangeAmount { get; set; } = 0.0M; |
|||
|
|||
[Column(InsertValueSql = "getdate()")] |
|||
public DateTime? CreateTime { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 分红比例
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,2)")] |
|||
public decimal DividendRatio { get; set; } = 0.0M; |
|||
|
|||
|
|||
public long? Id { get; set; } |
|||
|
|||
|
|||
public long OrderId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 订单利润
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal OrderProfit { get; set; } = 0.0M; |
|||
|
|||
|
|||
public long RobotId { get; set; } |
|||
|
|||
|
|||
public long UserId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 用户投资收益
|
|||
/// </summary>
|
|||
[Column(DbType = "decimal(18,8)")] |
|||
public decimal UserProfit { get; set; } = 0.0M; |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1 @@ |
|||
FreeSql.Generator -Razor 1 -NameOptions 1,0,0,0 -NameSpace Binance.TradeRobot.Model.Db -DB "SqlServer,data source=.;initial catalog=Binance.TradeRobot.DB;User Id=sa;Password=pc911103;TrustServerCertificate=true;pooling=true;max pool size=2" -FileName "{name}.cs" |
Loading…
Reference in new issue