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 { /// /// 交易OA服务 /// public class TradeOAService : YLBaseService { private static readonly IYcLogger logger = LogFactory.GetLogger(); public TradeOAService(OptUserInfo userInfo) : base(userInfo) { } public TradeOAService(YLBaseService baseService) : base(baseService) { } /// /// 处理单个交易的OA提交 /// /// 交易ID /// 处理结果 public async Task 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.code == 0) { oaResult.status = "提交成功"; oaResult.oa_fileid = oaResp.data?.requestid; oaResult.oa_msg = oaResp.data?.msg ?? "提交成功"; } else { oaResult.status = "提交失败"; oaResult.oa_msg = oaResp?.data?.msg ?? "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}" }; } } /// /// 上传附件到OA系统 /// /// 文件路径 /// 附件数据列表(包含 annexId 和 annexName) private async Task> UploadAttachmentAsync(string filePath) { try { // 从配置文件读取上传配置 var oaConfigSection = AppManager.GetConfiguration().GetSection("oa_confg"); var uploadUrl = oaConfigSection["UploadUrl"]; var systemToken = oaConfigSection["SystemToken"]; if (string.IsNullOrEmpty(uploadUrl)) { logger.Error("OA接口配置中UploadUrl为空"); throw new Exception("OA接口配置中上传地址为空"); } if (string.IsNullOrEmpty(systemToken)) { logger.Error("OA接口配置中SystemToken为空"); throw new Exception("OA接口配置中上传令牌为空"); } if (!File.Exists(filePath)) { logger.Error($"附件文件不存在:{filePath}"); throw new Exception("附件文件不存在"); } // 创建multipart/form-data请求 using (var httpClient = new HttpClient()) { // 设置超时时间为2分钟 httpClient.Timeout = TimeSpan.FromMinutes(2); using (var form = new MultipartFormDataContent()) { // 添加文件 var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); var fileName = Path.GetFileName(filePath); form.Add(new StreamContent(fileStream), "file", fileName); // 添加systemToken form.Add(new StringContent(systemToken), "systemToken"); // 发送请求 try { var response = await httpClient.PostAsync(uploadUrl, form); response.EnsureSuccessStatusCode(); // 确保响应成功 var responseContent = await response.Content.ReadAsStringAsync(); logger.Info($"附件上传接口返回: {responseContent}"); // 解析响应 try { var uploadResponse = JsonHelper.Deserialize(responseContent); if (uploadResponse != null) { if (uploadResponse.code == "0" && uploadResponse.data != null) { // 返回完整的附件数据列表(包含 annexId 和 annexName) logger.Info($"成功上传附件,获取到{uploadResponse.data.Count}个附件"); return uploadResponse.data; } else { logger.Error($"附件上传失败:{uploadResponse.status},code:{uploadResponse.code}"); throw new Exception($"附件上传失败:{uploadResponse.status}"); } } else { logger.Error("附件上传响应解析为空"); throw new Exception("附件上传响应解析失败"); } } catch (Exception ex) { logger.Error($"解析附件上传响应失败:{ex.Message},响应内容:{responseContent}", ex); throw new Exception($"解析附件上传响应失败:{ex.Message}"); } } catch (TaskCanceledException ex) { logger.Error($"上传附件时任务被取消:{ex.Message}", ex); throw new Exception("上传附件超时,请检查网络连接"); } catch (Exception ex) { logger.Error($"上传附件时发生异常:{ex.Message}", ex); throw; } } } } catch (Exception ex) { logger.Error($"上传附件时发生异常:{ex.Message}", ex); throw; } } /// /// 调用OA接口 /// /// 交易信息 /// 合同编号 /// 交易确认书文档 /// OA结果记录ID /// 是否成功 private async Task 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 subUrl = oaConfigSection["SubUrl"]; var systemToken = oaConfigSection["SystemToken"]; var systemName = oaConfigSection["SystemName"]; var isNextFlow = oaConfigSection["IsNextFlow"] ?? "0"; var flowId = oaConfigSection["FlowId"]; if (string.IsNullOrEmpty(baseUrl)) { logger.Error("OA接口配置中BaseUrl为空"); 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为空" } }; } // 处理附件 List attachmentDataList = null; if (tdoc != null && !string.IsNullOrEmpty(tdoc.AbsolutePath)) { try { attachmentDataList = await UploadAttachmentAsync(tdoc.AbsolutePath); logger.Info($"成功上传附件并获取到annexId:{string.Join(", ", attachmentDataList.Select(a => a.annexId))}"); } catch (Exception ex) { logger.Error($"上传附件失败:{tdoc.AbsolutePath},错误:{ex.Message}", ex); return new GuoLianOAResponse() { code = -1, data = new GuoLianOAData { msg = "上传附件失败" } }; } } //通过UserName找到LoginName var applicantLoginName = DbContextFactory.GetErpBaseContext().SystemUsers.Where(u => u.Name == UserInfo.UserName).Select(u => u.LoginName).FirstOrDefault(); // 构建dataMap var dataMap = new { title = $"固定收益部申请【{contractCode}】TRS交易确认书", urgentLevel = "正常",//未找到对应 applyDate = DateTime.Now.ToString("yyyy-MM-dd"), applicant = applicantLoginName, applicantComp = "国联民生证券总部", htlx = 0,//合同类型 0业务 1非业务 ywlx = 1, //业务类型 0资产管理业务金融产品筹设及调整事项合同(含ABS业务合同) 1金融交易类合同(除股票质押式回购业务部分购回、购回协议) 4采购类 3工会类 7党建活动类 2其他 6股票质押式回购业务部分购回、购回协议 8代销协议 htmj = 0, //合同密级 0一般 1秘密 2绝密 hehj = 0, //合同缓急 0一般 1紧急 2特急 //去除后缀 htmc = Path.GetFileNameWithoutExtension(tdoc.FileName),//合同名称 sfgljy = 1,//是否关联交易 0是 1否 jflx = 0,//甲方类型 0企业 1个人 jf = trade.ClientName,//甲方 yflx = 0,//乙方类型 0企业 1个人 yf = "国联民生证券股份有限公司",//乙方 sfdxtzdfqsjf = 1,//是否短信通知对方签署 0是 1否 sfdxtzyf = 1,//是否短信通知乙方 0是 1否 htje = trade.OriginalStockEqvNotional,//合同金额 htyxksrq = trade.StartDate?.ToString("yyyy-MM-dd"),//合同有效期开始日期 htyxjsrq = trade.ExerciseDate?.ToString("yyyy-MM-dd"),//合同有效期结束日期 bz = "人民币",//币种 htqdrq = DateTime.Now.ToString("yyyy-MM-dd"),//合同签订日期 sfqdljxy = 2, //是否签订廉洁协议 0已签 1未签 2无需签订 htbjjs = $"我司与{trade.ClientName}已完成NAFMII协议签署,并开展TRS交易:{contractCode},参与标的{trade.UnderlyingCode} {trade.UnderlyingName},参与标的数量面额{trade.OriginalStockEqvNotional}元人民币", //合同背景介绍 // 使用附件数据列表(包含 annexId 和 annexName) fj = attachmentDataList?.FirstOrDefault(), creater = applicantLoginName }; // 构建查询参数(不包含dataMap,dataMap放在请求体中) // 注意:Java后端使用驼峰命名,参数名需与后端@RequestParam注解的名称一致 var query = $"flowId={Uri.EscapeDataString(flowId)}" + $"&isNextFlow={Uri.EscapeDataString(isNextFlow ?? string.Empty)}" + $"&systemToken={Uri.EscapeDataString(systemToken)}" + $"&systemName={Uri.EscapeDataString(systemName ?? string.Empty)}"; var requestUrl = subUrl.Contains("?") ? $"{subUrl}&{query}" : $"{subUrl}?{query}"; logger.Info($"国联OA接口URL: {requestUrl}"); // 调用OA接口 (POST请求,dataMap放在请求体中) string response = null; try { // 设置较长的超时时间,默认15秒可能不够 var httpClient = new HttpClientWrap(baseUrl, 60); // 60秒超时 response = httpClient.PostJson(requestUrl, dataMap, null); logger.Info($"国联OA接口返回: {response}"); // 解析响应 var responseObj = JsonHelper.Deserialize(response); return responseObj; } catch (TaskCanceledException ex) { logger.Error($"调用OA接口时任务被取消:{ex.Message}", ex); return new GuoLianOAResponse() { code = -2, data = new GuoLianOAData { msg = "调用OA接口超时,请检查网络连接" } }; } catch (Exception ex) { logger.Error($"调用OA接口时发生异常:{ex.Message},响应内容:{response}", ex); return new GuoLianOAResponse() { code = -2, data = new GuoLianOAData { msg = $"调用OA接口时发生异常: {ex.Message}" } }; } } catch (Exception ex) { logger.Error($"调用OA接口时发生异常,交易ID:{trade.id},异常信息:{ex.Message}", ex); return new GuoLianOAResponse() { code = -2, data = new GuoLianOAData { msg = $"调用OA接口时发生异常: {ex.Message}" } }; } } } }