Merge branch 'glms/feature/1.4.2' of http://git.yiliantech.com/gitlab/otc-dev/zszq-trs into glms/feature/1.4.2

This commit is contained in:
hjhan
2026-08-12 09:48:53 +08:00
4 changed files with 131 additions and 10 deletions
@@ -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
+10 -4
View File
@@ -888,7 +888,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));
}
}
//当日有平仓或互换记录时,避免重复结算
@@ -1119,11 +1121,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);
}
/// <summary>
/// 保证金腿的 orginPv 维度重映射。
@@ -1332,7 +1336,9 @@ namespace YLErp.Modules.SwapModule
/// <param name="isAnnualized">是否年化</param>
/// <param name="annualDays">年化天数</param>
/// <returns></returns>
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;
@@ -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;
+119 -4
View File
@@ -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<string> 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<string>();
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<string> 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;
}
}
}
}