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