feat(swap): 债券计算器C#契约对齐——targetDate估值日透传·错误处理透传真实原因·链路日志

- BondCalcHepler.BondCalc 新增可选 targetDate(估值日) 参数并写入请求体,透传至代理 zszq-bond-oms 的 targetDate(→bond-calc settlementDate);不传则沿用代理默认 T+1,与 bond-oms-ui 契约一致。

- 新增 out errorMsg 重载:地址未配/无响应/success=false/业务错误码 errCode!=0/异常 均返回 null 并透出真实中文原因;日志级别由 Info 升为 Error,try/catch 包裹。

- BondController.CalcBond 把 errorMsg 透传给 JsonError,不再硬编码"计算价格失败"。

- BondCalcByDate(自动结算路径) 同步加 errCode/空守卫,避免回写坏数据。
This commit is contained in:
hjhan
2026-07-22 11:35:41 +08:00
parent e7c6e432e1
commit 0aad4c8040
2 changed files with 92 additions and 25 deletions
+87 -22
View File
@@ -1,4 +1,4 @@
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Asn1.Ocsp;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,44 +10,103 @@ namespace YLErp.Helpers
{
/// <summary>
/// 计算器帮助类
/// 调用链:zszq-trs → zszq-bond-oms(/calc/cal_bond_value) → bond-calc 微服务。
/// 契约(与 bond-oms-ui / zszq-bond-oms 保持一致):
/// 请求体 { bondId, price, priceType(DP全价/CP净价/YD收益率), targetDate?(估值日 yyyy-MM-dd,缺省代理取 T+1) }
/// 成功响应 data:{ errCode:0, errMsg:null, dirtyPrice(全价), cleanPrice(净价), ytm(收益率%) }
/// 失败响应:success=false 且 message=中文原因(债券不存在/信息不全/参数非法/服务异常)
/// </summary>
public class BondCalcHepler
{
private const string CalcUrl = "/calc/cal_bond_value";
/// <summary>
/// 计算器
/// </summary>
/// <returns></returns>
public static CalBondResult BondCalc(string underlyingCode,decimal price,string priceType="DP")
/// 计算器(债券净价/全价/收益率互算)。
/// 兼容旧调用方(如 RealtimePnlCalc):返回 CalBondResult,失败返回 null。
/// targetDate 可选:估值日(yyyy-MM-dd),不传则沿用计算器代理默认 T+1。
/// </summary>
public static CalBondResult BondCalc(string underlyingCode, decimal price, string priceType = "DP", string targetDate = null)
{
string _err;
return BondCalc(underlyingCode, price, priceType, out _err, targetDate);
}
/// <summary>
/// 计算器(带错误信息输出)。
/// 任何失败(地址未配/无响应/success=false/业务错误码 errCode!=0/异常)都返回 null
/// 并通过 errorMsg 带上**真实原因**,供上层(BondController)清晰反馈给用户。
/// 关键:即使 success=true,只要内部 errCode!=0(债券不存在/信息不全/参数非法)也视为失败,
/// 避免把 0/异常价格静默回写覆盖用户手工输入。
/// targetDate 可选:估值日(yyyy-MM-dd),透传给代理的 targetDate(底层 bond-calc 的 settlementDate);
/// 不传则代理默认取 T+1(下一工作日)。
/// </summary>
public static CalBondResult BondCalc(string underlyingCode, decimal price, string priceType, out string errorMsg, string targetDate = null)
{
errorMsg = null;
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
CalcBondRequest request = new CalcBondRequest()
{
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
var calculateUrl = "/calc/cal_bond_value";
CalcBondRequest request = new CalcBondRequest() {
bondId= underlyingCode,
price = price.ToString(),
priceType = priceType,
};
if (!string.IsNullOrEmpty(baseUrl))
bondId = underlyingCode,
price = price.ToString(),
priceType = priceType,
targetDate = targetDate,
};
if (string.IsNullOrEmpty(baseUrl))
{
errorMsg = "计算器服务地址未配置(BondOmsInterface_BaseUrl)";
return null;
}
try
{
LogFactory.GetLogger("BondCalcHepler").Info(
$"债券计算器请求: url={baseUrl}{CalcUrl} bondId={underlyingCode} price={price} priceType={priceType} targetDate={(string.IsNullOrEmpty(targetDate) ? "(T+1)" : targetDate)}");
var httpHelper = new HttpHelper(baseUrl, null);
// http 请求 Web项目接口
var result = httpHelper.PostRequestNoAuth<CalcBondRequest, CalcBondReponse>(calculateUrl, request).Result;
if (result != null && !result.success)
var result = httpHelper.PostRequestNoAuth<CalcBondRequest, CalcBondReponse>(CalcUrl, request).Result;
if (result == null)
{
LogFactory.GetLogger("BondCalcHepler").Info("计算器计算失败:" + result.message);
errorMsg = "计算器服务无响应";
LogFactory.GetLogger("BondCalcHepler").Error("债券计算器无响应,地址:" + baseUrl + CalcUrl);
return null;
}
else
if (!result.success)
{
return result.data;
errorMsg = result.message ?? "债券计算失败";
LogFactory.GetLogger("BondCalcHepler").Error("债券计算失败:" + errorMsg);
return null;
}
if (result.data == null)
{
errorMsg = "计算器返回数据为空";
LogFactory.GetLogger("BondCalcHepler").Error("债券计算器返回 data 为空");
return null;
}
// 业务层错误码(债券不存在 / 债券信息不全 / 参数非法),zszq-bond-oms 在 success=true 时仍可能带 errCode!=0
if (result.data.errCode != 0)
{
errorMsg = result.data.errMsg ?? "债券计算业务错误";
LogFactory.GetLogger("BondCalcHepler").Error("债券计算业务错误(code=" + result.data.errCode + ")" + errorMsg);
return null;
}
LogFactory.GetLogger("BondCalcHepler").Info(
$"债券计算器成功: bondId={underlyingCode} targetDate={targetDate} cleanPrice={result.data.cleanPrice} dirtyPrice={result.data.dirtyPrice} ytm={result.data.ytm}");
return result.data;
}
catch (Exception ex)
{
errorMsg = "请求债券计算器异常:" + ex.Message;
LogFactory.GetLogger("BondCalcHepler").Error("请求债券计算器时发生异常", ex);
return null;
}
return null;
}
/// <summary>
/// 按结算日计算(自动结算路径用)。与 BondCalc 同款健壮处理:
/// 地址未配/无响应/success=false/业务错误码/异常 均返回 null(不回写坏数据)。
/// </summary>
public static CalBondResult BondCalcByDate(string underlyingCode, decimal price, String targetDate, string priceType = "DP")
{
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
var calculateUrl = "/calc/cal_bond_value";
CalcBondRequest request = new CalcBondRequest()
{
bondId = underlyingCode,
@@ -61,7 +120,13 @@ namespace YLErp.Helpers
try
{
// http 请求 Web项目接口
var result = httpHelper.PostRequestNoAuth<CalcBondRequest, CalcBondReponse>(calculateUrl, request).Result;
var result = httpHelper.PostRequestNoAuth<CalcBondRequest, CalcBondReponse>(CalcUrl, request).Result;
if (result == null || !result.success || result.data == null || result.data.errCode != 0)
{
var msg = result != null ? (result.message ?? (result.data != null ? result.data.errMsg : null)) : "计算器服务无响应";
LogFactory.GetLogger("BondCalcHelper").Error("债券计算失败:" + msg);
return null;
}
return result.data;
}
catch (Exception ex)
+5 -3
View File
@@ -7,12 +7,14 @@ namespace YLErp.Web.Controllers
{
[AllowAnonymous]
public JsonResult CalcBond(string underlyingCode, decimal price, string priceType)
public JsonResult CalcBond(string underlyingCode, decimal price, string priceType, string targetDate = null)
{
var obj = BondCalcHepler.BondCalc(underlyingCode, price, priceType);
string errorMsg;
var obj = BondCalcHepler.BondCalc(underlyingCode, price, priceType, out errorMsg, targetDate);
if (obj == null)
{
return JsonError("计算价格失败");
// 透传计算器返回的真实原因(债券不存在/信息不全/参数非法/服务异常),前端以非阻塞 toast 展示
return JsonError(errorMsg ?? "计算价格失败");
}
return JsonSuccess("", obj);