diff --git a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs index ade5d22a..a4047271 100644 --- a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs +++ b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs @@ -471,7 +471,7 @@ namespace YLErp.Plugins.GuoLian.DocumentGenerator var notional = trade.OriginalStockEqvNotional ?? 0; var tradingFee = (double)swapPosition.PosiTradingFeePending; var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100; - dic["基本费率"] = basicFeeRate.ToString("0.##"); + dic["基本费率"] = basicFeeRate.ToString("0.####"); // 期初现金交换比例和金额(使用初始预付金数据) dic["期初现金交换比例"] = initialMarginPosition != null diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index c4bdd523..8d4f067a 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -886,7 +886,9 @@ namespace YLErp.Modules.SwapModule var consumedInterest = position.InterestType == (int)InterestTypeEnum.复利 ? GetConsumedInterest(td.id, position.id, endDate) : 0m; - interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, calcLast||newCalcLast, consumedInterest)); + interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, + closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, + calcLast||newCalcLast, consumedInterest)); } } //当日有平仓或互换记录时,避免重复结算 @@ -1117,11 +1119,13 @@ namespace YLErp.Modules.SwapModule preEod.ValueDate = td.StartDate.Value; if (calcFirst) { - preEod.ValueDate= preEod.ValueDate.AddDays(-1); + preEod.ValueDate = preEod.ValueDate.AddDays(-1); } } - return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal, closePrincipal, closePercent, annualDays, eventType, preEod, false, orginPv, calcFirst, calcLast, consumedInterest); + return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal, + closePrincipal, closePercent, annualDays, eventType, preEod, false, + orginPv, calcFirst, calcLast, consumedInterest); } /// /// 保证金腿的 orginPv 维度重映射。 @@ -1357,7 +1361,9 @@ namespace YLErp.Modules.SwapModule /// 是否年化 /// 年化天数 /// - public void CalcDailyCompoundInterest(DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m) + public void CalcDailyCompoundInterest(DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent, + int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, + ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m) { var startDate = position.PosiStartDate; decimal interestProfitSum = 0; diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs index 859824f8..8b6cb0ed 100644 --- a/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs @@ -369,7 +369,7 @@ namespace YLErp.Modules.SwapModule { interestStart = td.StartDate.Value; var exerciseDate = td.ExerciseDate.Value; - interestEnd = valueDate> exerciseDate? exerciseDate : valueDate; + interestEnd = valueDate > exerciseDate ? exerciseDate : valueDate; bool calcFirst = true; bool calcLast = true; diff --git a/YLErpWeb/App/ExceptionMiddleware.cs b/YLErpWeb/App/ExceptionMiddleware.cs index a0c469d2..86b10b6c 100644 --- a/YLErpWeb/App/ExceptionMiddleware.cs +++ b/YLErpWeb/App/ExceptionMiddleware.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Http; using System.Buffers; +using System.Text; +using System.Text.Json; namespace YLErp.Web.App { @@ -17,6 +19,11 @@ namespace YLErp.Web.App public async Task Invoke(HttpContext context) { + if (IsSwapTradeEditRequest(context.Request)) + { + context.Request.EnableBuffering(); + } + try { await _next.Invoke(context); @@ -41,9 +48,17 @@ namespace YLErp.Web.App if (serviceExpcetion == null || serviceExpcetion.IsFaultError) { - var result = await request.BodyReader.ReadAsync(); - var reqBody = ConvertBufferToString(result.Buffer); - LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};[body]:{reqBody}"); + if (IsSwapTradeEditRequest(request)) + { + var diagnostic = await GetSwapIntervalDiagnosticAsync(request); + LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};{diagnostic}"); + } + else + { + var result = await request.BodyReader.ReadAsync(); + var reqBody = ConvertBufferToString(result.Buffer); + LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};[body]:{reqBody}"); + } } } catch (Exception ex) @@ -78,6 +93,106 @@ namespace YLErp.Web.App return System.Text.Encoding.UTF8.GetString(span); } + private static bool IsSwapTradeEditRequest(HttpRequest request) + { + return string.Equals(request.Path.Value, "/swaptrade2/tradeEditJson", StringComparison.OrdinalIgnoreCase); + } + + private static async Task GetSwapIntervalDiagnosticAsync(HttpRequest request) + { + if (!request.Body.CanSeek) + { + return "[swap-interval-diagnostic]:request-body-unavailable"; + } + + request.Body.Position = 0; + using var reader = new StreamReader(request.Body, Encoding.UTF8, false, 1024, leaveOpen: true); + var requestBody = await reader.ReadToEndAsync(); + request.Body.Position = 0; + + if (string.IsNullOrWhiteSpace(requestBody)) + { + return "[swap-interval-diagnostic]:request-body-empty"; + } + + try + { + using var document = JsonDocument.Parse(requestBody); + if (!document.RootElement.TryGetProperty("swap_positions", out var positions) || positions.ValueKind != JsonValueKind.Array) + { + return "[swap-interval-diagnostic]:swap_positions-missing"; + } + + var invalidRates = new List(); + var positionIndex = 0; + foreach (var position in positions.EnumerateArray()) + { + var positionId = position.TryGetProperty("id", out var id) ? id.ToString() : "missing"; + AddInvalidRateDiagnostics(position, "SwapIntervalList", false, positionIndex, positionId, invalidRates); + AddInvalidRateDiagnostics(position, "InterestSwapInterval", true, positionIndex, positionId, invalidRates); + if (position.TryGetProperty("Obervation", out var observation)) + { + AddInvalidRateDiagnostics(observation, "Obervation.ObservationInterval", true, positionIndex, positionId, invalidRates); + } + if (invalidRates.Count >= 10) + { + break; + } + positionIndex++; + } + + return invalidRates.Count == 0 + ? "[swap-interval-diagnostic]:no-invalid-rate-in-payload" + : $"[swap-interval-diagnostic]:{string.Join(";", invalidRates)}"; + } + catch (JsonException) + { + return "[swap-interval-diagnostic]:request-json-invalid"; + } + } + + private static void AddInvalidRateDiagnostics(JsonElement position, string source, bool serializedJson, int positionIndex, string positionId, List invalidRates) + { + if (!position.TryGetProperty(source, out var intervals)) + { + return; + } + + if (serializedJson) + { + if (intervals.ValueKind != JsonValueKind.String) + { + return; + } + + try + { + using var document = JsonDocument.Parse(intervals.GetString()); + intervals = document.RootElement.Clone(); + } + catch (JsonException) + { + invalidRates.Add($"positionIndex={positionIndex},positionId={positionId},source={source},interval-json-invalid"); + return; + } + } + + if (intervals.ValueKind != JsonValueKind.Array) + { + return; + } + + var intervalIndex = 0; + foreach (var interval in intervals.EnumerateArray()) + { + if ((!interval.TryGetProperty("Rate", out var rate) || rate.ValueKind == JsonValueKind.Null) && invalidRates.Count < 10) + { + invalidRates.Add($"positionIndex={positionIndex},positionId={positionId},source={source},intervalIndex={intervalIndex},rate={(rate.ValueKind == JsonValueKind.Null ? "null" : "missing")}"); + } + intervalIndex++; + } + } + private static string GetInnerExceptionMessage(Exception ex) { var exceptionStr = ex.Message; @@ -89,4 +204,4 @@ namespace YLErp.Web.App return exceptionStr; } } -} \ No newline at end of file +}