#EQD-4516 国联民生-OA流程对接

This commit is contained in:
锦麟 王
2026-03-04 13:32:55 +08:00
parent e3011800df
commit 759dbef2a9
2 changed files with 86 additions and 123 deletions
+19
View File
@@ -22,4 +22,23 @@ namespace YLErp.Modules.TradeModule
public string url { get; set; }
public string fileid { get; set; }
}
/// <summary>
/// 国联OA接口响应模型
/// </summary>
public class GuoLianOAResponse
{
public int code { get; set; }
public GuoLianOAData data { get; set; }
public string status { get; set; }
}
/// <summary>
/// 国联OA接口响应数据模型
/// </summary>
public class GuoLianOAData
{
public string msg { get; set; }
public string requestid { get; set; }
}
}
+67 -123
View File
@@ -75,17 +75,16 @@ namespace YLErp.Modules.TradeModule
// 调用OA接口
var oaResp = await CallOAInterfaceAsync(trade, tcr?.ContractCode, tdoc, oaResult.id);
if (oaResp != null && oaResp.success)
if (oaResp != null && oaResp.code == 0)
{
oaResult.status = "提交成功";
oaResult.oa_fileid = oaResp.data?.fileid;
oaResult.oa_msg = oaResp.message;
oaResult.oa_url = oaResp.data?.url;
oaResult.oa_fileid = oaResp.data?.requestid;
oaResult.oa_msg = oaResp.data?.msg ?? "提交成功";
}
else
{
oaResult.status = "提交失败";
oaResult.oa_msg = oaResp?.message ?? "OA接口调用失败";
oaResult.oa_msg = oaResp?.data?.msg ?? "OA接口调用失败";
}
await DbContext.SaveChangesAsync();
@@ -106,7 +105,7 @@ namespace YLErp.Modules.TradeModule
/// <param name="tdoc">交易确认书文档</param>
/// <param name="oaResultId">OA结果记录ID</param>
/// <returns>是否成功</returns>
private async Task<OAResponse> CallOAInterfaceAsync(trade trade, string contractCode, trade_contract_document tdoc, int oaResultId)
private async Task<GuoLianOAResponse> CallOAInterfaceAsync(trade trade, string contractCode, trade_contract_document tdoc, int oaResultId)
{
try
{
@@ -114,33 +113,24 @@ namespace YLErp.Modules.TradeModule
var oaConfigSection = AppManager.GetConfiguration().GetSection("oa_confg");
var baseUrl = oaConfigSection["BaseUrl"];
var subUrl = oaConfigSection["SubUrl"];
var loginName = string.Empty;
using (var adminDb = new ErpBaseContext())
{
var userInfo = adminDb.SystemUsers.FirstOrDefault(x => x.Id == UserInfo.UserId);
loginName = userInfo?.OaAccount;
}
var systemKey = oaConfigSection["SystemKey"];
var objectClass = oaConfigSection["ObjectClass"];
var tradeType = oaConfigSection["TradeType"] ?? "2";
var businessType = oaConfigSection["BusinessType"] ?? "7";
var urgent = int.Parse(oaConfigSection["Urgent"] ?? "1");
var issend = int.Parse(oaConfigSection["Issend"] ?? "0");
var systemToken = oaConfigSection["SystemToken"];
var systemName = oaConfigSection["SystemName"];
var isNextFlow = oaConfigSection["IsNextFlow"] ?? "0";
if (string.IsNullOrEmpty(loginName))
{
logger.Error("OA账号未配置");
return new OAResponse() { data = null, success = false, message = "OA账号未配置" };
}
if (string.IsNullOrEmpty(baseUrl))
{
logger.Error("OA接口配置中BaseUrl为空");
return new OAResponse() { data = null, success = false, message = "OA接口配置中地址为空" };
return new GuoLianOAResponse() { code = -1, data = new GuoLianOAData { msg = "OA接口配置中地址为空" } };
}
if (string.IsNullOrEmpty(systemToken))
{
logger.Error("OA接口配置中SystemToken为空");
return new GuoLianOAResponse() { code = -1, data = new GuoLianOAData { msg = "OA接口配置中SystemToken为空" } };
}
// 处理附件
var attachments = new List<object>();
string attachmentBase64 = null;
string attachmentFileName = null;
if (tdoc != null && !string.IsNullOrEmpty(tdoc.AbsolutePath))
{
try
@@ -148,131 +138,85 @@ namespace YLErp.Modules.TradeModule
if (File.Exists(tdoc.AbsolutePath))
{
var fileBytes = await File.ReadAllBytesAsync(tdoc.AbsolutePath);
var base64Content = Convert.ToBase64String(fileBytes);
var fileName = Path.GetFileName(tdoc.AbsolutePath);
attachments.Add(new
{
nrtitle = fileName,
nrtype = "1",
content = base64Content
});
attachmentBase64 = Convert.ToBase64String(fileBytes);
attachmentFileName = Path.GetFileName(tdoc.AbsolutePath);
logger.Info($"成功读取附件文件:{tdoc.AbsolutePath},文件大小:{fileBytes.Length} 字节");
}
else
{
logger.Error($"附件文件不存在:{tdoc.AbsolutePath}");
return new OAResponse() { data = null, success = false, message = $"附件文件不存在" };
return new GuoLianOAResponse() { code = -1, data = new GuoLianOAData { msg = "附件文件不存在" } };
}
}
catch (Exception ex)
{
logger.Error($"读取附件文件失败:{tdoc.AbsolutePath},错误:{ex.Message}", ex);
return new OAResponse() { data = null, success = false, message = $"读取附件文件失败" };
return new GuoLianOAResponse() { code = -1, data = new GuoLianOAData { msg = "读取附件文件失败" } };
}
}
// 计算同一客户、同一交易日期、同一多空方向名义本金汇总
// 1. 先获取当前交易的多空方向
var currentPosition = DbContext.swap_position.AsNoTracking()
.Where(sp => sp.SwapTradeId == trade.id && sp.IsInitial && sp.PositionType > 0 && !sp.Invalid)
.FirstOrDefault();
var currentPositionType = currentPosition.PositionType;
// 2. 查询同一客户、同一交易日期的所有有效交易
var sameClientTrades = DbContext.trade.AsNoTracking()
.Where(t => t.ClientId == trade.ClientId
&& t.TradeDate == trade.TradeDate
&& t.ValidState != "InValid");
var exerciseDate = sameClientTrades.Select(x => x.ExerciseDate).OrderBy(o => o).FirstOrDefault();
// 3. 关联持仓表,筛选相同多空方向的持仓
var matchingPositions = from t in sameClientTrades
join sp in DbContext.swap_position.AsNoTracking()
on t.id equals sp.SwapTradeId
where sp.PositionType == currentPositionType
&& sp.IsInitial && sp.PositionType > 0 && !sp.Invalid
select t;
// 4. 计算名义本金汇总和加权平均保证金率
var totalNotionalPrincipal = matchingPositions.Sum(x => x.OriginalStockEqvNotional);
// 计算加权平均保证金率
decimal marginRate = 0;
if (totalNotionalPrincipal > 0)
// 构建dataMap
var dataMap = new
{
decimal weightedMarginSum = 0;
foreach (var t in matchingPositions)
title = $"固定收益部申请【{contractCode}】TRS交易确认书",
urgentLevel = "正常",
applyDate = DateTime.Now.ToString("yyyyMMdd"),
applicant = UserInfo.UserName,
applicantComp = "国联民生证券总部",
contractType = "业务",
businessType = "金融交易类合同(除股票质押式回购业务部分购回、购回协议)",
contractSecretLevel = "一般",
contractUrgentLevel = "一般",
//去除后缀
contractName = Path.GetFileNameWithoutExtension(tdoc.FileName),
isRelatedTransaction = "否",
partyAType = "企业",
partyA = trade.ClientName,
partyBType = "企业",
partyB = "国联民生证券股份有限公司",
partyASmsNotify = "否",
partyBSmsNotify = "否",
contractAmount = trade.OriginalStockEqvNotional,
contractBeginDate = trade.StartDate?.ToString("yyyyMMdd"),
contractEndDate = trade.ExerciseDate?.ToString("yyyyMMdd"),
currency = "人民币",
contractSignDate = DateTime.Now.ToString("yyyyMMdd"),
signIntegrityAgreement = "无需签订",
contractBackground = $"我司与{trade.ClientName}已完成NAFMII协议签署,并开展TRS交易:{contractCode},参与标的{trade.UnderlyingCode} {trade.UnderlyingName},参与标的数量面额{trade.OriginalStockEqvNotional}元人民币",
attachments = string.IsNullOrEmpty(attachmentBase64) ? null : new[]
{
var marginDetail = UnderlyingHelper.GetApplicableMarginRate(t.ClientId, t.UnderlyingCode, t.TradeDate.Value);
var rate = marginDetail?.init_rate ?? 0;
weightedMarginSum += rate * Convert.ToDecimal(t.OriginalStockEqvNotional);
new
{
fileName = attachmentFileName,
fileBytes = attachmentBase64
}
}
marginRate = weightedMarginSum / Convert.ToDecimal(totalNotionalPrincipal);
// marginRate=Math.Round(marginRate, 4); // 保留四位小数
}
};
var posiTypeStr = currentPositionType == (int)PositionTypeFlag.Long ? "买入" : "卖出";
var underlyingType = sameClientTrades.Count() > 0 ? "利率债" : trade.UnderlyingInstrumentTypeCn;
string bgContent = @$"<p>各位领导:</p>
<p style=""margin-left: 40px"">经友好协商,我司拟与交易对手开展以下场外利率收益互换交易:</p>
<p style=""margin-left: 40px;"">浮动收益支付方:&nbsp;浙商证券</p>
<p style=""margin-left: 40px;"">浮动收益接收方:&nbsp;{trade.ClientName}</p>
<p style=""margin-left: 111px;"">标的:&nbsp;{underlyingType}</p>
<p style=""margin-left: 111px;"">方向:&nbsp;{posiTypeStr}</p>
<p style=""margin-left: 95px;"">起始日:&nbsp;{trade.StartDate?.ToString("yyyy/M/d")}</p>
<p style=""margin-left: 95px;"">&nbsp;{exerciseDate?.ToString("yyyy/M/d")}</p>
<p style=""margin-left: 48px;"">&nbsp;{totalNotionalPrincipal:N0}</p>
<p style=""margin-left: 48px;"">&nbsp;{trade.ClientName}</p>
<p>{(marginRate * 100).ToString("0.##")}%线</p>";
// 构建OA请求参数
var oaRequest = new
{
loginname = loginName,
systemkey = systemKey,
objectclass = objectClass,
title = $"关于场外利率债收益互换交易的审批盖章申请({trade.StartDate.Value.ToString("yyyyMMdd")}-{trade.ClientName})",
urgent = urgent,
issend = issend,
copyattfileid = "",
je = (double)totalNotionalPrincipal,
filerela = new object[0],
groupno = "",
extinfo = new
{
busdata = new
{
BM1 = "",
BM2 = "",
BGRQ = trade.TradeDate.Value.ToString("yyyy-MM-dd"),
NGR1 = "",
NGR2 = "",
CKDX1 = "",
CKDX2 = "",
LB = "",
JYLX = tradeType,//自主对冲
YWLX = businessType,//收益互换
QSBGNR = bgContent,
MX = new object[0],
SQMX = new object[0]
},
attachments = attachments.ToArray()
}
isnextflow = isNextFlow,
systemToken = systemToken,
systemName = systemName,
dataMap = JsonHelper.Serialize(dataMap)
};
logger.Info($"OA接口参数:{JsonHelper.Serialize(oaRequest)}");
// 调用OA接口
logger.Info($"国联OA接口参数: {JsonHelper.Serialize(oaRequest)}");
// 调用OA接口 (GET请求)
var httpClient = new HttpClientWrap(baseUrl);
var response = httpClient.PostJson(subUrl, oaRequest, null);// fileDraft
logger.Info($"OA接口返回:{response}");
var response = httpClient.PostJson(subUrl, oaRequest,null);
logger.Info($"国联OA接口返回: {response}");
// 解析响应
var responseObj = JsonHelper.Deserialize<OAResponse>(response);
var responseObj = JsonHelper.Deserialize<GuoLianOAResponse>(response);
return responseObj;
}
catch (Exception ex)
{
logger.Error($"调用OA接口时发生异常,交易ID{trade.id},异常信息:{ex.Message}", ex);
return new OAResponse() { data = null, success = false, message = $"调用OA接口时发生异常" };
return new GuoLianOAResponse() { code = -2, data = new GuoLianOAData { msg = $"调用OA接口时发生异常: {ex.Message}" } };
}
}
}