Files
zszq-trs/YLErpDAL/Modules/TradeModule/TradeOAService.cs
T

273 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BaseOUDAL;
using NPOI.SS.Formula.Functions;
using Qdp.Foundation.Utilities;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Model;
using YLErp.Modules.ApiModule;
using YLErp.Modules.TradeDalModule;
using YLErp.Modules.UnderlyingModule;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.TradeModule
{
/// <summary>
/// 交易OA服务
/// </summary>
public class TradeOAService : YLBaseService
{
private static readonly IYcLogger logger = LogFactory.GetLogger<TradeOAService>();
public TradeOAService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeOAService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 处理单个交易的OA提交
/// </summary>
/// <param name="tradeId">交易ID</param>
/// <returns>处理结果</returns>
public async Task<TradeOAResult> ProcessSingleOAAsync(int tradeId)
{
try
{
// 获取交易信息
var trade = DbContext.trade.FirstOrDefault(x=>x.id==tradeId);
if (trade == null)
{
return new TradeOAResult {TradeId=tradeId, Success = false, Message = "未找到交易信息" };
}
// 获取交易确认书信息
var tcr = DbContext.trade_contract_r.Where(t => t.Type == ContractTypeEnum.Trade && t.IsValid&&t.TradeId==tradeId)
.FirstOrDefault();
trade_contract_document tdoc = null;
if (tcr!=null)
{
tdoc = DbContext.trade_contract_document.Where(t => t.Code == tcr.ContractCode&&t.Type==tcr.Type).FirstOrDefault();
}
// 将之前的OA记录设置为无效
var existingOAResults = DbContext.tradeContractOaResult.Where(x => x.trade_id == tradeId && x.is_valid == true).ToList();
foreach (var existingResult in existingOAResults)
{
existingResult.is_valid = false;
existingResult.SetOpt(UserInfo);
}
// 创建OA结果记录
var oaResult = new trade_contract_oa_result
{
trade_id = tradeId,
contract_code = tcr?.ContractCode,
status = "提交中",
is_valid = true,
};
oaResult.SetOpt(UserInfo);
DbContext.tradeContractOaResult.Add(oaResult);
await DbContext.SaveChangesAsync();
// 调用OA接口
var oaResp = await CallOAInterfaceAsync(trade, tcr?.ContractCode, tdoc, oaResult.id);
if (oaResp != null&& oaResp.success)
{
oaResult.status = "提交成功";
oaResult.oa_fileid = oaResp.data?.fileid;
oaResult.oa_msg = oaResp.message;
oaResult.oa_url = oaResp.data?.url;
}
else
{
oaResult.status = "提交失败";
oaResult.oa_msg = oaResp?.message ?? "OA接口调用失败";
}
await DbContext.SaveChangesAsync();
return new TradeOAResult { TradeId = tradeId, Success = oaResult.status== "提交成功", Message = oaResult.oa_msg };
}
catch (Exception ex)
{
logger.Error($"处理交易ID {tradeId} 的OA时发生错误: {ex.Message}", ex);
return new TradeOAResult { TradeId = tradeId, Success = false, Message = $"提交OA失败: {ex.Message}" };
}
}
/// <summary>
/// 调用OA接口
/// </summary>
/// <param name="trade">交易信息</param>
/// <param name="contractCode">合同编号</param>
/// <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)
{
try
{
// 获取OA配置
var oaConfigSection = AppManager.GetConfiguration().GetSection("oa_confg");
var baseUrl = oaConfigSection["BaseUrl"];
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 urgent = int.Parse(oaConfigSection["Urgent"] ?? "1");
var issend = int.Parse(oaConfigSection["Issend"] ?? "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接口配置中地址为空" };
}
// 处理附件
var attachments = new List<object>();
if (tdoc != null && !string.IsNullOrEmpty(tdoc.AbsolutePath))
{
try
{
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
});
logger.Info($"成功读取附件文件:{tdoc.AbsolutePath},文件大小:{fileBytes.Length} 字节");
}
else
{
logger.Error($"附件文件不存在:{tdoc.AbsolutePath}");
return new OAResponse() { data = null, success = false, message = $"附件文件不存在" };
}
}
catch (Exception ex)
{
logger.Error($"读取附件文件失败:{tdoc.AbsolutePath},错误:{ex.Message}", ex);
return new OAResponse() { data = null, success = false, message = $"读取附件文件失败" };
}
}
// 计算同一客户、同一交易日期、同一多空方向名义本金汇总
// 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)
{
decimal weightedMarginSum = 0;
foreach (var t in matchingPositions) {
var marginDetail = UnderlyingHelper.GetApplicableMarginRate(t.ClientId, t.UnderlyingCode, t.TradeDate.Value);
var rate = marginDetail?.init_rate ?? 0;
weightedMarginSum += rate*Convert.ToDecimal(t.OriginalStockEqvNotional);
}
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.TradeDate.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 = "",
QSBGNR = bgContent,
MX = new object[0],
SQMX = new object[0]
},
attachments = attachments.ToArray()
}
};
logger.Info($"OA接口参数:{JsonHelper.Serialize(oaRequest)}");
// 调用OA接口
var httpClient = new HttpClientWrap(baseUrl);
var response = httpClient.PostJson("/ZSZQ/fileDraft", oaRequest, null);// fileDraft
logger.Info($"OA接口返回:{response}");
// 解析响应
var responseObj = JsonHelper.Deserialize<OAResponse>(response);
return responseObj;
}
catch (Exception ex)
{
logger.Error($"调用OA接口时发生异常,交易ID{trade.id},异常信息:{ex.Message}", ex);
return new OAResponse() { data = null, success = false, message = $"调用OA接口时发生异常" };
}
}
}
}