#EQD-4516 国联民生-OA流程对接
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;"">浮动收益支付方: 浙商证券</p>
|
||||
<p style=""margin-left: 40px;"">浮动收益接收方: {trade.ClientName}</p>
|
||||
<p style=""margin-left: 111px;"">标的: {underlyingType}</p>
|
||||
<p style=""margin-left: 111px;"">方向: {posiTypeStr}</p>
|
||||
<p style=""margin-left: 95px;"">起始日: {trade.StartDate?.ToString("yyyy/M/d")}</p>
|
||||
<p style=""margin-left: 95px;"">到期日: {exerciseDate?.ToString("yyyy/M/d")}</p>
|
||||
<p style=""margin-left: 48px;"">合约名义本金: {totalNotionalPrincipal:N0}元</p>
|
||||
<p style=""margin-left: 48px;"">保证金支付方: {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}" } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user