- SwapCalcTrace 改为双模式:保留全局静态开关(单元测试用 IsEnabled/Dump),
新增请求级 AsyncLocal 缓冲(EnableForRequest/DumpForRequest/ClearForRequest),
由中间件按请求开关,不跨请求串扰。
- 新增 SwapCalcTraceMiddleware:开启方式三选一——URL ?swaptrace=1 /
请求头 X-SwapCalc-Trace:1 / 标记文件 App_Data/swapcalctrace.on(无需重启);
Response.OnStarting 时把逐日计息过程写入响应头 X-SwapCalc-Trace(超长落
logs/swapcalctrace-YYYYMMDD.log 并在头给路径),同时写服务器日志。
开发者在浏览器“网络”面板即可查看,零前端改码、零行为影响、性能可忽略。
- Program.cs 在 ExceptionMiddleware 后注册该中间件。
- 删除 CalcDailyCompoundInterestByEod 遗留垃圾日志 LogFactory.GetLogger("test").Error("lksafhasdhfjas")。
- CalcDailySimpleInterest 两处追踪守卫改为 IsEnabled||IsRequestEnabled。
- 既有 PrepaidPrincipalCloseTraceTest / ClosingChainTraceTest 2/2 仍全绿。
回答用户两点:① dynomicPrincipal 公式字段含义;② 原 Trace 需重启才能开,
现改为前端 otcdebug 式(?swaptrace=1) 即可在响应头看到逐步计息过程。
246 lines
9.9 KiB
C#
246 lines
9.9 KiB
C#
using Microsoft.AspNetCore.Authentication.Cookies;
|
||
using Microsoft.AspNetCore.Http.Features;
|
||
using Microsoft.AspNetCore.ResponseCompression;
|
||
using Microsoft.AspNetCore.StaticFiles;
|
||
using Microsoft.Extensions.FileProviders;
|
||
using Microsoft.Extensions.WebEncoders;
|
||
using Microsoft.IdentityModel.Tokens;
|
||
using Newtonsoft.Json;
|
||
using NLog;
|
||
using NLog.Web;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using System.Text.Encodings.Web;
|
||
using System.Text.Unicode;
|
||
using YieldChain.Commons;
|
||
using YLErp.Abstract;
|
||
using YLErp.Serialization;
|
||
using YLErp.Services.CashRecordModule;
|
||
using YLErp.Web.App;
|
||
using YLErp.Web.App.KafkaTask;
|
||
using YLErp.Web.Hubs;
|
||
using YLErp.Web.Middleware;
|
||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||
|
||
JsonConvert.DefaultSettings = JsonHelper.GetDefaultSettings;
|
||
|
||
var logger = LogManager.Setup().GetCurrentClassLogger();
|
||
|
||
logger.Debug("init main");
|
||
|
||
try
|
||
{
|
||
var builder = WebApplication.CreateBuilder(args);
|
||
var configuration = builder.Configuration;
|
||
|
||
// Add services to the container.
|
||
builder.Services.AddHttpClient();
|
||
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
|
||
{
|
||
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||
options.JsonSerializerOptions.Converters.Add(new AntiXssConverter());
|
||
options.JsonSerializerOptions.Converters.Add(CustomDoubleConvert.Default);
|
||
options.JsonSerializerOptions.Converters.Add(new CustomDateTimeConverter());
|
||
}).AddRazorRuntimeCompilation();
|
||
|
||
//视图渲染绑定中文乱码问题
|
||
builder.Services.Configure<WebEncoderOptions>(options => options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All));
|
||
builder.Services.Configure<FormOptions>(options =>
|
||
{
|
||
options.ValueCountLimit = 1000000;
|
||
options.ValueLengthLimit = 1024 * 1024 * 100; // 100MB max len form data
|
||
options.MultipartBodyLengthLimit = int.MaxValue; //不限制文件上传大小
|
||
});
|
||
builder.Services.Configure<MvcOptions>(options => options.MaxModelBindingCollectionSize = 1000000);
|
||
builder.Services.Configure<KafkaConfig>(builder.Configuration.GetSection("KafkaConfig"));
|
||
builder.Services.Configure<ExternalCashFlowOptions>(builder.Configuration.GetSection("ExternalCashFlow"));
|
||
builder.Services.AddScoped<IViewRenderService, ViewRenderService>();
|
||
builder.Services.AddSingleton<IKafkaProduce, KafkaProduceHelper>();
|
||
builder.Services.AddTransient<IExternalCashFlowService, ExternalCashFlowService>();
|
||
builder.Services.AddResponseCompression(options =>
|
||
{
|
||
options.Providers.Add<BrotliCompressionProvider>();
|
||
options.Providers.Add<GzipCompressionProvider>();
|
||
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml" });
|
||
});
|
||
|
||
builder.Services.AddAuthentication(AuthHelper.CookieAuthType)
|
||
.AddCookie(AuthHelper.CookieAuthType, options =>
|
||
{
|
||
options.LoginPath = "/Account/Login/";
|
||
options.AccessDeniedPath = "/Account/Login/";
|
||
options.ExpireTimeSpan = TimeSpan.FromSeconds(AuthHelper.ExpireInSeconds);
|
||
options.SlidingExpiration = true;
|
||
options.Events = new CookieAuthenticationEvents
|
||
{
|
||
OnRedirectToLogin = context =>
|
||
{
|
||
var accept = context.Request.Headers["Accept"].ToString();
|
||
var xrw = context.Request.Headers["X-Requested-With"].ToString();
|
||
var isAjax = xrw == "XMLHttpRequest" || !accept.Contains("text/html");
|
||
logger.Info($"[CookieAuth] OnRedirectToLogin Path={context.Request.Path} Accept={accept} X-Requested-With={xrw} IsAjax={isAjax}");
|
||
if (isAjax)
|
||
{
|
||
context.Response.StatusCode = 401;
|
||
return Task.CompletedTask;
|
||
}
|
||
context.Response.Redirect(context.RedirectUri);
|
||
return Task.CompletedTask;
|
||
},
|
||
OnRedirectToAccessDenied = context =>
|
||
{
|
||
var accept = context.Request.Headers["Accept"].ToString();
|
||
var xrw = context.Request.Headers["X-Requested-With"].ToString();
|
||
var isAjax = xrw == "XMLHttpRequest" || !accept.Contains("text/html");
|
||
logger.Info($"[CookieAuth] OnRedirectToAccessDenied Path={context.Request.Path} Accept={accept} X-Requested-With={xrw} IsAjax={isAjax}");
|
||
if (isAjax)
|
||
{
|
||
context.Response.StatusCode = 403;
|
||
return Task.CompletedTask;
|
||
}
|
||
context.Response.Redirect(context.RedirectUri);
|
||
return Task.CompletedTask;
|
||
}
|
||
};
|
||
})
|
||
.AddJwtBearer(AuthHelper.JwtAuthType, options =>
|
||
{
|
||
options.TokenValidationParameters = new TokenValidationParameters
|
||
{
|
||
ValidateLifetime = true, //是否验证失效时间
|
||
ClockSkew = TimeSpan.FromSeconds(AuthHelper.ExpireInSeconds),
|
||
ValidateAudience = false,
|
||
ValidateIssuer = false, //是否验证Issuer
|
||
ValidateIssuerSigningKey = true, //是否验证SecurityKey
|
||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthHelper.JwtSecretKey))
|
||
};
|
||
});
|
||
|
||
builder.Services.AddAuthorization(options =>
|
||
{
|
||
options.AddPolicy(AuthHelper.CookieAuthType, policy => policy.RequireAssertion(context => ErpAuthorizeHandler.Default.Handle(context)));
|
||
options.AddPolicy(AuthHelper.JwtAuthType, policy => policy.RequireAssertion(context => ErpAuthorizeHandler.Default.Handle(context)));
|
||
});
|
||
|
||
//NLog: Setup NLog for Dependency injection
|
||
builder.Logging.ClearProviders();
|
||
builder.Logging.SetMinimumLevel(LogLevel.Trace);
|
||
builder.Host.UseNLog();
|
||
|
||
// Save IServerCollection
|
||
YLServiceLocator.SetServiceCollection(builder.Services);
|
||
|
||
Server.Initialize(builder.Environment, builder.Configuration);
|
||
|
||
Startup.Start(builder.Configuration);
|
||
builder.Services.AddHostedService<ClientBalanceTask>();
|
||
|
||
builder.Services.AddHostedService<ClientReskCheckKafkaTask>();
|
||
builder.Services.AddHostedService<HostedTaskService>();
|
||
builder.Services.AddHostedService<ClientBalanceMonitorTotalKafkaTask>();
|
||
builder.Services.AddHostedService<ClientBalanceMonitorSingleKafkaTask>();
|
||
|
||
builder.Services.AddHostedService<TrsExchangeTradeConsumerKafkaTask>();
|
||
builder.Services.AddHostedService<HedgingOrderCancelConsumerKafkaTask>();
|
||
|
||
|
||
builder.Services.AddHostedService<HolidayConsumerKafkaTask>();
|
||
builder.Services.AddHostedService<TrsSwapFlowConsumerKafkaTask>();
|
||
|
||
builder.Services.AddCaptcha(builder.Configuration, option =>
|
||
{
|
||
option.ImageOption.Width = 256; // 验证码宽度
|
||
option.ImageOption.Height = 34; // 验证码高度
|
||
option.ExpirySeconds = 60; // 验证码过期时间
|
||
option.IgnoreCase = true; // 比较时是否忽略大小写
|
||
option.ImageOption.BubbleCount = 0;
|
||
option.ImageOption.InterferenceLineCount = 3; // 干扰线数量
|
||
});
|
||
builder.Services.AddSignalR();
|
||
//============================================
|
||
|
||
var app = builder.Build();
|
||
|
||
// Save IServerProvider
|
||
YLServiceLocator.SetServiceProvider(app.Services);
|
||
|
||
// Configure the HTTP request pipeline.
|
||
if (app.Environment.IsDevelopment())
|
||
{
|
||
}
|
||
|
||
app.UseMiddleware<ExceptionMiddleware>();
|
||
|
||
// 计息计算过程追踪(?swaptrace=1 / X-SwapCalc-Trace:1 开启,详见 SwapCalcTraceMiddleware)
|
||
app.UseMiddleware<SwapCalcTraceMiddleware>();
|
||
|
||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||
{
|
||
// Linux 路径区分大小写,需要对静态文件做处理
|
||
app.UseMiddleware<StaticUrlMiddleware>();
|
||
}
|
||
|
||
app.UseStaticFiles();
|
||
|
||
app.UseRouting();
|
||
|
||
app.UseAuthentication();
|
||
|
||
// Token续期中间件
|
||
app.UseMiddleware<RefreshTokenMiddleware>();
|
||
|
||
app.UseAuthorization();
|
||
app.UseEndpoints(endpoints =>
|
||
{
|
||
endpoints.MapHub<SwapFlowCombookingHub>("/swapflow/combookinghub"); // 映射Hub路径
|
||
endpoints.MapHub<SwapFlowResetHub>("/swapflow/resethub"); // 映射Hub路径
|
||
endpoints.MapHub<SwapConfirmSendEmailHub>("/tradeconfirm/sendemailhub"); // 映射Hub路径
|
||
endpoints.MapHub<TradeConfirmSendOAHub>("/tradeconfirm/sendoahub"); // 映射OA Hub路径
|
||
});
|
||
var provider = new FileExtensionContentTypeProvider();
|
||
provider.Mappings[".pdf"] = "application/pdf";
|
||
provider.Mappings[".doc"] = "application/msword";
|
||
provider.Mappings[".docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||
provider.Mappings[".xls"] = "application/vnd.ms-excel";
|
||
provider.Mappings[".xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||
|
||
app.UseStaticFiles(new StaticFileOptions
|
||
{
|
||
FileProvider = new PhysicalFileProvider(Server.MapPath("App_Docs")),
|
||
RequestPath = "/App_Docs",
|
||
ContentTypeProvider = provider
|
||
});
|
||
|
||
app.UseResponseCompression();
|
||
|
||
app.MapAreaControllerRoute(
|
||
name: "MyAreaAdmin",
|
||
areaName: "Admin",
|
||
pattern: "Admin/{controller=Home}/{action=Index}/{id?}");
|
||
|
||
app.MapControllerRoute(
|
||
name: "mapi",
|
||
pattern: "m/api/{*path}");
|
||
|
||
app.MapControllerRoute(
|
||
name: "v3",
|
||
pattern: "v3/{*path}",
|
||
new { controller = "V3", action = "Index" });
|
||
|
||
app.MapControllerRoute(
|
||
name: "default",
|
||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||
|
||
app.Run();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// NLog: catch setup errors
|
||
logger.Error(ex, "Stopped program because of exception");
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||
LogManager.Shutdown();
|
||
} |