feat: 收益互换开平仓及收益结算接入OA移动审批
#EQD-6425 国联民生-衍生品系统移动审批(提交OA流程) - 审批节点支持配置“关联OA移动审批” - 创建OA流程并记录请求、requestId、状态及异常备注 - 支持OA轮询同步审批通过、退回和归档状态 - 本地通过、拒绝、撤回时同步强制归档未结束OA流程 - 增加审批列表OA备注展示及本地Mock联调接口 - 新增OA审批记录表和数据库升级脚本 - OA移动审批使用独立查询地址及isnextflow配置,不影响交易确认书OA
This commit is contained in:
@@ -70,5 +70,9 @@ namespace YLErp.DBModels
|
||||
/// </summary>
|
||||
[DisplayName("节点触发条件")]
|
||||
public string triggerCondition { get; set; }
|
||||
|
||||
/// <summary>是否在进入该节点时关联国联民生 OA 移动审批。</summary>
|
||||
public bool isOaApproval { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace YLErp.DBModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 衍生品交易审批节点对应的国联民生 OA 流程记录。
|
||||
/// </summary>
|
||||
[Table("trade_approval_oa_result")]
|
||||
public class trade_approval_oa_result : DBModelBaseV6
|
||||
{
|
||||
public int trade_id { get; set; }
|
||||
public int approval_process_id { get; set; }
|
||||
public string process_type { get; set; }
|
||||
public int? applicant_id { get; set; }
|
||||
public string applicant_login_name { get; set; }
|
||||
public string oa_fileid { get; set; }
|
||||
public string status { get; set; }
|
||||
public string oa_msg { get; set; }
|
||||
public string request_payload { get; set; }
|
||||
public string last_response { get; set; }
|
||||
public DateTime? last_query_time { get; set; }
|
||||
public bool is_valid { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -406,6 +406,7 @@ namespace YLErp.BLL
|
||||
public DbSet<client_margin_config> clientMarginConfig { get; set; }
|
||||
public DbSet<client_margin_detail> clientMarginDetail { get; set; }
|
||||
public DbSet<trade_contract_oa_result> tradeContractOaResult { get; set; }
|
||||
public DbSet<trade_approval_oa_result> tradeApprovalOaResult { get; set; }
|
||||
public DbSet<BondPayment> bondPayment { get; set; }
|
||||
|
||||
public DbSet<glms_risk_rule> glms_risk_rule { get; set; }
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace YLErp.Model
|
||||
|
||||
public int? ProcessRoleId { get; set; }
|
||||
public string ProcessRoleName { get; set; }
|
||||
public string OaRemark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 验证当前审批角色是否有开仓(交易)复核权限
|
||||
|
||||
@@ -1250,6 +1250,10 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!new TradeApprovalOAService(this).ArchiveActiveForTrade(tradeObj, out var errorMessage))
|
||||
{
|
||||
throw new ServiceException(errorMessage);
|
||||
}
|
||||
var eventTypes = new List<int>() { (int)SwapEventTypeEnum.修改交易, (int)SwapEventTypeEnum.新增交易, (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 };
|
||||
var lastEvent = DbContext.swap_event.Where(x => x.SwapTradeId == intid && eventTypes.Contains(x.EventType) && !x.Invalid).OrderByDescending(o => o.OptTime).FirstOrDefault();
|
||||
if (tradeObj.TradeStatus == ConsTrade.平仓待复核 || tradeObj.TradeStatus == ConsTrade.互换待复核)
|
||||
|
||||
@@ -59,7 +59,8 @@ namespace YLErp.Modules.SystemModule
|
||||
parentNode = item.parentNode,
|
||||
approvalCondition = item.approvalCondition,
|
||||
conditionConfig = item.conditionConfig,
|
||||
triggerCondition = item.triggerCondition
|
||||
triggerCondition = item.triggerCondition,
|
||||
isOaApproval = item.isOaApproval
|
||||
}).ToList();
|
||||
|
||||
DbContext.approvalprocess.AddRange(list);
|
||||
@@ -790,6 +791,8 @@ namespace YLErp.Modules.SystemModule
|
||||
|
||||
/// <summary>节点触发条件(JSON),需求①:满足才进入该审批节点,不满足则跳过。</summary>
|
||||
public string triggerCondition { get; set; }
|
||||
|
||||
public bool isOaApproval { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 审批流程修改节点
|
||||
|
||||
@@ -263,6 +263,10 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
td.OptName = UserName;
|
||||
td.OptDate = OptDate;
|
||||
int passStatus = new ApprovalProcessService(OptUser).CheckTradeApprovalStep(td, OptUser.UserId);
|
||||
if (passStatus != 0 && passStatus != -3)
|
||||
{
|
||||
new TradeApprovalOAService(this).EnsureForCurrentNode(td);
|
||||
}
|
||||
if (passStatus == 0)
|
||||
{
|
||||
td.TradeStatus = ConsTrade.确认成交;
|
||||
|
||||
@@ -56,6 +56,23 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
throw new ServiceException("该交易状态已变更,不能被审批,请先刷新页面");
|
||||
}
|
||||
|
||||
if (!req.isFromOa && (req.status == "pass" || req.status == "reject"))
|
||||
{
|
||||
var currentNode = TradeProcessByCategory(td)
|
||||
.Where(x => x.order == td.ProcessOrderId && (x.node == 0 || x.node == td.ProcessOrderBranch))
|
||||
.OrderByDescending(x => x.node == td.ProcessOrderBranch)
|
||||
.FirstOrDefault();
|
||||
if (currentNode?.isOaApproval == true)
|
||||
{
|
||||
var oa = DbContext.tradeApprovalOaResult.FirstOrDefault(x => x.trade_id == td.id
|
||||
&& x.approval_process_id == currentNode.id
|
||||
&& x.is_valid
|
||||
&& (x.status == "提交成功" || x.status == "同步中" || x.status == "归档中"));
|
||||
if (oa != null && !new TradeApprovalOAService(this).ArchiveForLocalAction(oa, out var errorMessage))
|
||||
throw new ServiceException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.status == "pass")
|
||||
{
|
||||
return TradePass(req, td);
|
||||
@@ -179,6 +196,13 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
return result;
|
||||
}
|
||||
|
||||
// 关联 OA 的节点必须先成功创建 OA 流程,才允许本地审批推进到该节点。
|
||||
if (nextOrder.isOaApproval && td.TradeType == "收益互换")
|
||||
{
|
||||
// OA 创建是节点通过后的外部动作;明确失败只记录在 OA 结果表,不能回滚本地节点通过。
|
||||
new TradeApprovalOAService(this).CreateForNode(td, nextOrder);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// 继续审批流转
|
||||
//-----------------------------------------------
|
||||
@@ -788,6 +812,7 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
public bool notNeedOperationHistory;
|
||||
public bool isPartialExercise;
|
||||
public bool isExpire = false;
|
||||
public bool isFromOa;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var td in revokeTrades)
|
||||
{
|
||||
ArchiveActiveOaOrThrow(td);
|
||||
}
|
||||
|
||||
var tradeids = revokeTrades.Select(n => n.id);
|
||||
var revokeTrade_cashs = DbContext.trade_cash.Where(c => c.ValidState == "InValid" && !c.IsDeleted && tradeids.Contains(c.TradeId)).ToList();
|
||||
revokeTrade_cashs.ForEach(x =>
|
||||
@@ -94,6 +99,7 @@
|
||||
var Withdraws = DbContext.trade.Where(t => tradeIds.Contains(t.id)).ToList();
|
||||
foreach (var td in Withdraws)
|
||||
{
|
||||
ArchiveActiveOaOrThrow(td);
|
||||
var changesTradeStatus = DbContext.TradeAuditLog.Where(x => (x.OptType == "确认交易" || x.OptType == "交易特批-确认交易") && x.TradeId == td.id).OrderByDescending(x => x.id)?.FirstOrDefault()?.Changes;
|
||||
td.TradeStatus = changesTradeStatus == ConsTrade.新增待确认 ? ConsTrade.新增待确认 : ConsTrade.修改待确认;
|
||||
td.CheckStatus = null;
|
||||
@@ -103,6 +109,12 @@
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private void ArchiveActiveOaOrThrow(trade td)
|
||||
{
|
||||
if (!new TradeApprovalOAService(this).ArchiveActiveForTrade(td, out var errorMessage))
|
||||
throw new ServiceException(errorMessage);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取包含黑箱结构的子交易集合
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
using BaseOUDAL;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Consts;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Modules.ApiModule;
|
||||
using YLErp.Modules.TradeModule.DealModule;
|
||||
|
||||
namespace YLErp.Modules.TradeModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 收益互换审批节点的国联民生 OA 对接。
|
||||
/// 本期只负责 HTTP 创建、查询和归档;审批结果由轮询任务回写本地流程。
|
||||
/// </summary>
|
||||
public class TradeApprovalOAService : YLBaseService
|
||||
{
|
||||
private const string Pending = "提交成功";
|
||||
private const string Syncing = "同步中";
|
||||
private const string Ending = "归档中";
|
||||
private readonly IYcLogger _logger = LogFactory.GetLogger<TradeApprovalOAService>();
|
||||
|
||||
public TradeApprovalOAService(OptUserInfo userInfo) : base(userInfo) { }
|
||||
public TradeApprovalOAService(YLBaseService baseService) : base(baseService) { }
|
||||
|
||||
public bool IsSupported(trade trade)
|
||||
{
|
||||
return trade != null && trade.TradeType == "收益互换";
|
||||
}
|
||||
|
||||
public trade_approval_oa_result CreateForNode(trade trade, approvalprocess node)
|
||||
{
|
||||
if (!IsSupported(trade) || node == null || !node.isOaApproval)
|
||||
return null;
|
||||
|
||||
var scenario = node.processType == "TradeProcess"
|
||||
? "开仓"
|
||||
: (trade.TradeStatus == ConsTrade.平仓待复核 ? "平仓" : "收益结算");
|
||||
_logger.Info($"开始创建 OA 移动审批,交易:{trade.id},节点:{node.id},场景:{scenario}");
|
||||
var record = new trade_approval_oa_result
|
||||
{
|
||||
trade_id = trade.id,
|
||||
approval_process_id = node.id,
|
||||
process_type = node.processType,
|
||||
applicant_id = UserId,
|
||||
applicant_login_name = Config("Applicant"),
|
||||
status = "提交中",
|
||||
is_valid = true
|
||||
};
|
||||
record.SetOpt(UserInfo);
|
||||
DbContext.tradeApprovalOaResult.Add(record);
|
||||
DbContext.SaveChanges();
|
||||
|
||||
try
|
||||
{
|
||||
var creator = Config("Creator");
|
||||
var applicant = Config("Applicant");
|
||||
if (string.IsNullOrWhiteSpace(creator) || string.IsNullOrWhiteSpace(applicant))
|
||||
throw new ServiceException("OA 配置缺少 Creator 或 Applicant");
|
||||
|
||||
var spr = GetSprLoginName(node);
|
||||
var payload = BuildPayload(trade, creator, applicant, spr, scenario);
|
||||
record.request_payload = JsonHelper.Serialize(payload);
|
||||
var responseText = Post("SubUrl", payload);
|
||||
record.last_response = responseText;
|
||||
var response = JObject.Parse(responseText);
|
||||
var responseData = response["data"] as JObject;
|
||||
var responseCode = (int?)response["code"];
|
||||
var responseStatus = (string)response["status"];
|
||||
_logger.Info($"OA 创建流程原始响应,交易:{trade.id},节点:{node.id},响应:{responseText}");
|
||||
if ((int?)response["code"] != 0 || !string.Equals((string)response["status"], "SUCCESS", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
record.status = "提交失败";
|
||||
record.oa_msg = (string)responseData?["msg"] ?? (string)response["msg"] ?? "OA 创建流程失败";
|
||||
_logger.Error($"创建 OA 移动审批返回失败,交易:{trade.id},节点:{node.id},code:{responseCode},status:{responseStatus}");
|
||||
}
|
||||
else
|
||||
{
|
||||
record.status = Pending;
|
||||
record.oa_fileid = (string)responseData?["requestid"] ?? (string)responseData?["requestId"];
|
||||
record.oa_msg = (string)responseData?["msg"] ?? (string)response["msg"];
|
||||
if (string.IsNullOrWhiteSpace(record.oa_fileid))
|
||||
{
|
||||
record.status = "提交失败";
|
||||
_logger.Error($"创建 OA 移动审批未返回 requestId,交易:{trade.id},节点:{node.id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Info($"创建 OA 移动审批成功,交易:{trade.id},节点:{node.id},requestId:{record.oa_fileid}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
record.status = "提交失败";
|
||||
record.oa_msg = ex.Message;
|
||||
_logger.Error($"创建 OA 移动审批失败,交易:{trade.id},节点:{node.id}", ex);
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交易首次进入审批时,确保当前 OA 节点已创建对应流程。
|
||||
/// </summary>
|
||||
public trade_approval_oa_result EnsureForCurrentNode(trade trade)
|
||||
{
|
||||
if (!IsSupported(trade) || trade.ProcessOrderId <= 0)
|
||||
return null;
|
||||
|
||||
var node = GetCurrentNode(trade);
|
||||
if (node?.isOaApproval != true)
|
||||
return null;
|
||||
|
||||
var hasActiveRecord = DbContext.tradeApprovalOaResult.Any(x => x.trade_id == trade.id
|
||||
&& x.approval_process_id == node.id
|
||||
&& x.is_valid
|
||||
&& (x.status == "提交中" || x.status == Pending || x.status == Syncing || x.status == Ending));
|
||||
return hasActiveRecord ? null : CreateForNode(trade, node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地撤回前归档交易仍在审批中的 OA 流程。
|
||||
/// </summary>
|
||||
public bool ArchiveActiveForTrade(trade trade, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (!IsSupported(trade))
|
||||
return true;
|
||||
|
||||
var records = DbContext.tradeApprovalOaResult
|
||||
.Where(x => x.trade_id == trade.id
|
||||
&& x.is_valid
|
||||
&& !string.IsNullOrWhiteSpace(x.oa_fileid)
|
||||
&& (x.status == Pending || x.status == Syncing || x.status == Ending))
|
||||
.OrderBy(x => x.id)
|
||||
.ToList();
|
||||
if (records.Count == 0)
|
||||
{
|
||||
_logger.Info($"本地撤回前未找到待归档 OA 流程,交易:{trade.id}");
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
if (!ArchiveForLocalAction(record, out errorMessage))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地审批或撤回前归档 OA。通过条件更新抢占记录,避免与轮询回写同时推进同一节点。
|
||||
/// </summary>
|
||||
public bool ArchiveForLocalAction(trade_approval_oa_result record, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (record == null)
|
||||
return true;
|
||||
|
||||
_logger.Info($"本地操作前开始归档 OA 流程,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
|
||||
if (!TryChangeStatus(record.id, Pending, Ending, null))
|
||||
{
|
||||
_logger.Info($"本地操作前归档 OA 流程未抢占记录,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
errorMessage = "OA 审批结果正在同步,请稍后再试";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ForceEnd(record.oa_fileid))
|
||||
{
|
||||
TryChangeStatus(record.id, Ending, Pending, "OA 流程归档失败,等待重试");
|
||||
_logger.Error($"本地操作前归档 OA 流程失败,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
errorMessage = "OA 流程归档失败,暂不能执行本地审批操作";
|
||||
return false;
|
||||
}
|
||||
|
||||
TryChangeStatus(record.id, Ending, "已归档", "本地操作前已归档 OA 流程");
|
||||
_logger.Info($"本地操作前归档 OA 流程成功,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public JObject Query(string requestId)
|
||||
{
|
||||
var query = "requestId=" + Uri.EscapeDataString(requestId ?? string.Empty)
|
||||
+ "&systemToken=" + Uri.EscapeDataString(Config("SystemToken"))
|
||||
+ "&systemName=" + Uri.EscapeDataString(Config("SystemName"));
|
||||
var queryUrl = Config("TradeApprovalQueryUrl");
|
||||
if (string.IsNullOrWhiteSpace(queryUrl))
|
||||
throw new ServiceException("OA 配置缺少 TradeApprovalQueryUrl");
|
||||
var url = queryUrl + (queryUrl.Contains("?") ? "&" : "?") + query;
|
||||
var client = new HttpClientWrap(Config("BaseUrl"), 60);
|
||||
return JObject.Parse(client.Get(url, null));
|
||||
}
|
||||
|
||||
public bool ForceEnd(string requestId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 客户 OA 的 requestId 由 @RequestParam 接收,必须放在 URL QueryString。
|
||||
var response = JObject.Parse(Post("ForceEndUrl", new { }, requestId));
|
||||
var code = (int?)response["code"];
|
||||
var status = (string)response["status"];
|
||||
var responseData = response["data"] as JObject;
|
||||
var resultCode = (string)responseData?["resultcode"];
|
||||
var success = code == 0
|
||||
&& string.Equals((string)response["status"], "SUCCESS", StringComparison.OrdinalIgnoreCase)
|
||||
&& resultCode == "0";
|
||||
_logger.Info($"OA 强制归档返回,requestId:{requestId},code:{code},status:{status},resultcode:{resultCode},success:{success}");
|
||||
return success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"归档 OA 流程失败,requestId:{requestId}", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> SyncPendingStatuses(string requestId = null)
|
||||
{
|
||||
var messages = new List<string>();
|
||||
var staleAt = DateTime.Now.AddMinutes(-5);
|
||||
var pending = DbContext.tradeApprovalOaResult
|
||||
.Where(x => x.is_valid
|
||||
&& !string.IsNullOrWhiteSpace(x.oa_fileid)
|
||||
&& (string.IsNullOrWhiteSpace(requestId) || x.oa_fileid == requestId)
|
||||
&& (x.status == Pending || (x.status == Syncing && (x.last_query_time == null || x.last_query_time < staleAt))))
|
||||
.ToList();
|
||||
_logger.Info($"开始轮询 OA 移动审批状态,requestId:{requestId ?? "全部"},待查询数量:{pending.Count}");
|
||||
foreach (var record in pending)
|
||||
{
|
||||
var claimed = false;
|
||||
try
|
||||
{
|
||||
_logger.Info($"查询 OA 移动审批状态,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
var response = Query(record.oa_fileid);
|
||||
record.last_response = response.ToString();
|
||||
var flow = response["data"] is JArray data
|
||||
? data.FirstOrDefault(x => (string)x["requestId"] == record.oa_fileid) as JObject
|
||||
: null;
|
||||
var flowStatusType = (string)flow?["flowStatusType"];
|
||||
var flowNode = (string)flow?["flowNode"];
|
||||
var isReturned = flowStatusType == "0" && flowNode == "退回";
|
||||
// 1=批准;3=OA 批准后的自然归档。TRS 主动归档的记录不会进入本次查询。
|
||||
var isApproved = flowStatusType == "1" || flowStatusType == "3";
|
||||
_logger.Info($"OA 移动审批查询结果,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid},flowStatusType:{flowStatusType},flowNode:{flowNode},判定:{(isReturned ? "退回" : isApproved ? "通过" : "等待")}");
|
||||
if (!isApproved && !isReturned)
|
||||
{
|
||||
record.last_query_time = DateTime.Now;
|
||||
DbContext.SaveChanges();
|
||||
continue;
|
||||
}
|
||||
|
||||
DbContext.SaveChanges();
|
||||
if (!TryClaimForSync(record.id))
|
||||
{
|
||||
_logger.Info($"OA 审批结果同步未抢占记录,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid}");
|
||||
continue;
|
||||
}
|
||||
claimed = true;
|
||||
|
||||
var trade = DbContext.trade.Find(record.trade_id);
|
||||
if (trade == null)
|
||||
{
|
||||
SetClaimedStatus(record.id, "同步失败", "交易不存在");
|
||||
continue;
|
||||
}
|
||||
if (GetCurrentNode(trade)?.id != record.approval_process_id)
|
||||
{
|
||||
SetClaimedStatus(record.id, "同步忽略", "当前审批节点已变化,忽略 OA 结果");
|
||||
continue;
|
||||
}
|
||||
var result = new TradeOpenService(this).UpdateTradeProcessLog(new TradeOpenReqModel
|
||||
{
|
||||
tradeId = trade.id,
|
||||
status = isReturned ? "reject" : "pass",
|
||||
comments = isReturned ? "OA 移动审批退回" : "OA 移动审批通过",
|
||||
isFromOa = true,
|
||||
notNeedOperationHistory = false
|
||||
});
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
|
||||
{
|
||||
SetClaimedStatus(record.id, Pending, result.ErrorMsg);
|
||||
_logger.Error($"OA 审批结果回写本地失败,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid},原因:{result.ErrorMsg}");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetClaimedStatus(record.id, isReturned ? "退回" : "通过", null);
|
||||
_logger.Info($"OA 审批结果回写本地成功,交易:{record.trade_id},节点:{record.approval_process_id},requestId:{record.oa_fileid},结果:{(isReturned ? "退回" : "通过")}");
|
||||
}
|
||||
messages.Add(record.trade_id + ":" + (string.IsNullOrWhiteSpace(result.ErrorMsg) ? (isReturned ? "退回" : "通过") : Pending));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (claimed)
|
||||
{
|
||||
SetClaimedStatus(record.id, Pending, ex.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.last_query_time = DateTime.Now;
|
||||
record.oa_msg = ex.Message;
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
_logger.Error($"轮询 OA 审批状态失败,requestId:{record.oa_fileid}", ex);
|
||||
}
|
||||
}
|
||||
_logger.Info($"OA 移动审批状态轮询结束,已处理数量:{messages.Count}");
|
||||
return messages;
|
||||
}
|
||||
|
||||
private string Post(string urlKey, object payload, string requestId = null)
|
||||
{
|
||||
var path = Config(urlKey);
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
path = urlKey == "ForceEndUrl" ? "/gateway/oaflow/forceEndOaFlow" : "/gateway/oaflow/createOaFlow";
|
||||
var query = "systemToken=" + Uri.EscapeDataString(Config("SystemToken"))
|
||||
+ "&systemName=" + Uri.EscapeDataString(Config("SystemName"));
|
||||
if (urlKey == "ForceEndUrl")
|
||||
query += "&requestId=" + Uri.EscapeDataString(requestId ?? string.Empty);
|
||||
else
|
||||
query += "&flowId=" + Uri.EscapeDataString(Config("MobileFlowId", "245103"))
|
||||
+ "&isnextflow=" + Uri.EscapeDataString(Config("TradeApprovalIsNextFlow", "0"));
|
||||
var url = path + (path.Contains("?") ? "&" : "?") + query;
|
||||
return new HttpClientWrap(Config("BaseUrl"), 60).PostJson(url, payload, null);
|
||||
}
|
||||
|
||||
private string Config(string key, string fallback = "")
|
||||
{
|
||||
if (key == "BaseUrl" || key == "SubUrl" || key == "TradeApprovalQueryUrl" || key == "ForceEndUrl")
|
||||
{
|
||||
var mockEnabled = bool.TryParse(AppManager.GetConfiguration()["TradeApprovalOaMock:Enabled"], out var enabled) && enabled;
|
||||
var mockValue = AppManager.GetConfiguration()[$"TradeApprovalOaMock:{key}"];
|
||||
if (mockEnabled && !string.IsNullOrWhiteSpace(mockValue))
|
||||
return mockValue;
|
||||
}
|
||||
var value = AppManager.GetConfiguration()[$"oa_confg:{key}"];
|
||||
return string.IsNullOrWhiteSpace(value) ? fallback : value;
|
||||
}
|
||||
|
||||
private approvalprocess GetCurrentNode(trade trade)
|
||||
{
|
||||
if (trade == null) return null;
|
||||
var processType = IsCloseScenario(trade) ? "CloseProcess" : "TradeProcess";
|
||||
return DbContext.approvalprocess
|
||||
.Where(x => x.processType == processType
|
||||
&& x.order == trade.ProcessOrderId
|
||||
&& (x.node == trade.ProcessOrderBranch || x.node == 0))
|
||||
.OrderByDescending(x => x.node == trade.ProcessOrderBranch)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static bool IsCloseScenario(trade trade)
|
||||
{
|
||||
return trade.TradeStatus == ConsTrade.平仓待复核
|
||||
|| trade.TradeStatus == ConsTrade.行权待复核
|
||||
|| trade.TradeStatus == ConsTrade.互换待复核;
|
||||
}
|
||||
|
||||
private bool TryClaimForSync(int recordId)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
return DbContext.Database.ExecuteSqlRaw(
|
||||
"UPDATE trade_approval_oa_result SET status = {0}, last_query_time = {1} " +
|
||||
"WHERE id = {2} AND is_valid = 1 AND (status = {3} OR (status = {0} AND (last_query_time IS NULL OR last_query_time < {4})))",
|
||||
Syncing, now, recordId, Pending, now.AddMinutes(-5)) == 1;
|
||||
}
|
||||
|
||||
private bool TryChangeStatus(int recordId, string expectedStatus, string targetStatus, string message)
|
||||
{
|
||||
return DbContext.Database.ExecuteSqlRaw(
|
||||
"UPDATE trade_approval_oa_result SET status = {0}, oa_msg = {1} WHERE id = {2} AND is_valid = 1 AND status = {3}",
|
||||
targetStatus, message, recordId, expectedStatus) == 1;
|
||||
}
|
||||
|
||||
private void SetClaimedStatus(int recordId, string status, string message)
|
||||
{
|
||||
DbContext.Database.ExecuteSqlRaw(
|
||||
"UPDATE trade_approval_oa_result SET status = {0}, oa_msg = {1} WHERE id = {2} AND is_valid = 1 AND status = {3}",
|
||||
status, message, recordId, Syncing);
|
||||
}
|
||||
|
||||
private string GetSprLoginName(approvalprocess node)
|
||||
{
|
||||
using var baseDb = DbContextFactory.GetErpBaseContext();
|
||||
var users = (from roleUser in baseDb.RoleUsers
|
||||
join user in baseDb.SystemUsers on roleUser.UserId equals user.Id
|
||||
where roleUser.RoleId == node.roleId && user.State == (int)UserState.Enabled
|
||||
select user.LoginName).ToList();
|
||||
if (users.Count == 0)
|
||||
throw new ServiceException($"关联 OA 的审批角色未配置启用用户,角色ID:{node.roleId}");
|
||||
if (users.Count > 1)
|
||||
throw new ServiceException($"关联 OA 的审批角色配置了多个启用用户,角色ID:{node.roleId}");
|
||||
if (string.IsNullOrWhiteSpace(users[0]))
|
||||
throw new ServiceException($"关联 OA 的审批用户未配置登录名,角色ID:{node.roleId}");
|
||||
return users[0];
|
||||
}
|
||||
|
||||
private Dictionary<string, object> BuildPayload(trade trade, string creator, string applicant, string spr, string scenario)
|
||||
{
|
||||
var positions = DbContext.swap_position
|
||||
.Where(x => x.SwapTradeId == trade.id && x.IsInitial && !x.Invalid)
|
||||
.ToList();
|
||||
var floatPosition = positions.FirstOrDefault(x => x.IsInitial && !string.IsNullOrWhiteSpace(x.UnderlyingCode));
|
||||
var underlyingName = floatPosition == null
|
||||
? string.Empty
|
||||
: DbContext.underlying_manager.Where(x => x.UnderlyingCode == floatPosition.UnderlyingCode)
|
||||
.Select(x => x.UnderlyingName)
|
||||
.FirstOrDefault() ?? string.Empty;
|
||||
var interestPositions = positions.Where(x => string.IsNullOrWhiteSpace(x.UnderlyingCode)
|
||||
&& x.InterestMode != (int)InterestModeEnum.初始预付金
|
||||
&& x.InterestMode != (int)InterestModeEnum.追加预付金
|
||||
&& x.InterestMode != (int)InterestModeEnum.Unknown).ToList();
|
||||
var marginPositions = positions.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金
|
||||
|| x.InterestMode == (int)InterestModeEnum.追加预付金).ToList();
|
||||
var tradeDate = trade.TradeDate?.ToString("yyyy-MM-dd") ?? string.Empty;
|
||||
var startDate = floatPosition?.PosiStartDate.ToString("yyyy-MM-dd") ?? string.Empty;
|
||||
var maturityDate = floatPosition?.PosiMatuirityDate?.ToString("yyyy-MM-dd") ?? string.Empty;
|
||||
var contractDays = floatPosition?.PosiMatuirityDate == null
|
||||
? string.Empty
|
||||
: ((floatPosition.PosiMatuirityDate.Value.Date - floatPosition.PosiStartDate.Date).Days).ToString();
|
||||
|
||||
var elementLines = new List<string>
|
||||
{
|
||||
"交易日期=" + tradeDate,
|
||||
"审批角色=衍生品负责人",
|
||||
"交易编号=" + (trade.TradeNumber ?? string.Empty),
|
||||
"对手方=" + (trade.ClientName ?? string.Empty),
|
||||
"交易类型=" + scenario,
|
||||
"浮动端多空头=" + (floatPosition == null ? string.Empty : floatPosition.PositionType == 1 ? "多头" : "空头"),
|
||||
"利息端方向=" + JoinValues(interestPositions.Select(x => x.InterestDirection == 1 ? "收取" : "支付")),
|
||||
"簿记账户=" + (trade.AssetBookName ?? string.Empty),
|
||||
"结构类型=" + (trade.StructureType ?? string.Empty),
|
||||
"标的代码=" + (floatPosition?.UnderlyingCode ?? string.Empty),
|
||||
"标的名称=" + underlyingName,
|
||||
"期初收益率=" + (floatPosition?.InitYtm?.ToString("0.####%") ?? string.Empty),
|
||||
"期初价格=" + (floatPosition?.PosiNetPrice.ToString("0.####") ?? string.Empty),
|
||||
"行权方式=" + (trade.GetExerciseModeCn() ?? string.Empty),
|
||||
"合约期限=" + contractDays,
|
||||
"数量=" + (floatPosition?.PosiQuantity.ToString("0.####") ?? string.Empty),
|
||||
"起始日=" + startDate,
|
||||
"到期日=" + maturityDate,
|
||||
"交易员=" + (trade.TraderName ?? string.Empty),
|
||||
"支付日期=" + JoinValues(marginPositions.Select(x => x.HappenDate?.ToString("yyyy-MM-dd") ?? string.Empty)),
|
||||
"期初预付金=" + JoinValues(marginPositions.Select(x => x.InterestPrincipalFix.ToString("0.00"))),
|
||||
"期初预付金方向=" + JoinValues(marginPositions.Select(x => x.InterestDirection == 1 ? "收取" : "支付"))
|
||||
};
|
||||
|
||||
if (scenario == "开仓")
|
||||
{
|
||||
elementLines.Add("平仓金额=");
|
||||
elementLines.Add("平仓日期=");
|
||||
}
|
||||
else
|
||||
{
|
||||
var eventType = scenario == "平仓" ? (int)SwapEventTypeEnum.平仓 : (int)SwapEventTypeEnum.互换;
|
||||
var swapEvent = DbContext.swap_event.Where(x => x.SwapTradeId == trade.id && !x.Invalid && x.EventType == eventType)
|
||||
.OrderByDescending(x => x.id).FirstOrDefault();
|
||||
var unwind = string.IsNullOrWhiteSpace(swapEvent?.EventData) ? null : JsonHelper.Deserialize<UnwindData>(swapEvent.EventData);
|
||||
elementLines.Add("平仓金额=" + (unwind?.SwapCloseAmount.ToString("0.##") ?? ""));
|
||||
elementLines.Add("平仓日期=" + (unwind?.UnwindDate?.ToString("yyyy-MM-dd") ?? swapEvent?.ValueDate.ToString("yyyy-MM-dd") ?? ""));
|
||||
}
|
||||
|
||||
var elements = string.Join("<br/>", elementLines);
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
["title"] = "衍生品系统TRS移动审批-" + trade.TradeNumber,
|
||||
["creater"] = creator,
|
||||
["applicant"] = applicant,
|
||||
["spr"] = spr,
|
||||
["applyDate"] = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||
["elements"] = elements
|
||||
};
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static string JoinValues(IEnumerable<string> values)
|
||||
{
|
||||
return string.Join(";", values.Where(x => !string.IsNullOrWhiteSpace(x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,6 +287,13 @@ namespace YLErp.BLL
|
||||
.Select(a => new { a.order, a.roleId })
|
||||
.ToList()
|
||||
.ToDictionary(a => a.order, a => a.roleId);
|
||||
var approvalNodes = db.approvalprocess
|
||||
.Select(a => new { a.id, a.processType, a.order, a.node })
|
||||
.ToList();
|
||||
var oaRecords = db.tradeApprovalOaResult
|
||||
.Where(x => swapTradeIds.Contains(x.trade_id) && x.is_valid)
|
||||
.Select(x => new { x.id, x.trade_id, x.approval_process_id, x.status, x.oa_msg })
|
||||
.ToList();
|
||||
|
||||
foreach (var tradeLinq in retListResult.rows)
|
||||
{
|
||||
@@ -305,6 +312,29 @@ namespace YLErp.BLL
|
||||
tradeLinq.ProcessRoleName = name;
|
||||
}
|
||||
|
||||
if (tradeLinq.TradeType == "收益互换")
|
||||
{
|
||||
var processType = tradeLinq.TradeStatus == "平仓待复核" || tradeLinq.TradeStatus == "行权待复核" || tradeLinq.TradeStatus == "互换待复核"
|
||||
? "CloseProcess"
|
||||
: "TradeProcess";
|
||||
var currentNode = approvalNodes
|
||||
.Where(x => x.processType == processType
|
||||
&& x.order == tradeLinq.ProcessOrderId
|
||||
&& (x.node == tradeLinq.ProcessOrderBranch || x.node == 0))
|
||||
.OrderByDescending(x => x.node == tradeLinq.ProcessOrderBranch)
|
||||
.FirstOrDefault();
|
||||
var oa = currentNode == null ? null : oaRecords
|
||||
.Where(x => x.trade_id == tradeLinq.id && x.approval_process_id == currentNode.id)
|
||||
.OrderByDescending(x => x.id)
|
||||
.FirstOrDefault();
|
||||
if (oa != null)
|
||||
{
|
||||
tradeLinq.OaRemark = string.IsNullOrWhiteSpace(oa.oa_msg)
|
||||
? oa.status
|
||||
: oa.status + ":" + oa.oa_msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (tradeLinq.TradeType == "远期" || tradeLinq.TradeType == "掉期")
|
||||
{
|
||||
var option = db.trade_forward.FirstOrDefault(x => x.TradeId == tradeLinq.id);
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace YLErp.Modules.TradeModule
|
||||
// 需求①:进入审批流程时即应用触发条件——从起始节点开始,跳过所有不满足触发条件的节点。
|
||||
// 若所有节点均不满足 → 直接审批通过(无需任何人审核)。
|
||||
ApplyTriggerFromStart(tradeProcess, td, userId);
|
||||
new TradeApprovalOAService(this).EnsureForCurrentNode(td);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -334,6 +334,9 @@
|
||||
<span>审批规则</span>
|
||||
<input :id="'ruleType'+item.Index+item.node" v-model="item.ApprovalRules" type="text" name="selectRule" data-placeholder="" style="width: 200px; height: 20px" multiple="" />
|
||||
</div>
|
||||
<div v-if="item.Type === 'TradeProcess'">
|
||||
<label style="margin-left: 14px;"><input type="checkbox" v-model="item.isOaApproval" /> 关联 OA 移动审批</label>
|
||||
</div>
|
||||
<div class="trigger-condition-box">
|
||||
<div class="trigger-condition-title">
|
||||
<span>审批条件</span>
|
||||
@@ -426,6 +429,9 @@
|
||||
<span>审批规则</span>
|
||||
<input :id="'ruleType'+childSecond.Index+childSecond.node" v-model="childSecond.ApprovalRules" type="text" name="selectRule" data-placeholder="" style="width: 200px; height: 20px" multiple="" />
|
||||
</div>
|
||||
<div v-if="childSecond.Type === 'TradeProcess'">
|
||||
<label style="margin-left: 14px;"><input type="checkbox" v-model="childSecond.isOaApproval" /> 关联 OA 移动审批</label>
|
||||
</div>
|
||||
<div class="trigger-condition-box">
|
||||
<div class="trigger-condition-title">
|
||||
<span>审批条件</span>
|
||||
@@ -527,6 +533,9 @@
|
||||
<span>审批规则</span>
|
||||
<input :id="'closeRuleType'+item.Index+item.node" v-model="item.ApprovalRules" type="text" name="selectRule" data-placeholder="" style="width: 200px; height: 20px" multiple="" />
|
||||
</div>
|
||||
<div v-if="item.Type === 'CloseProcess'">
|
||||
<label style="margin-left: 14px;"><input type="checkbox" v-model="item.isOaApproval" /> 关联 OA 移动审批</label>
|
||||
</div>
|
||||
<div class="trigger-condition-box">
|
||||
<div class="trigger-condition-title">
|
||||
<span>审批条件</span>
|
||||
@@ -619,6 +628,9 @@
|
||||
<span>审批规则</span>
|
||||
<input :id="'closeRuleType'+childSecond.Index+childSecond.node" v-model="childSecond.ApprovalRules" type="text" name="selectRule" data-placeholder="" style="width: 200px; height: 20px" multiple="" />
|
||||
</div>
|
||||
<div v-if="childSecond.Type === 'CloseProcess'">
|
||||
<label style="margin-left: 14px;"><input type="checkbox" v-model="childSecond.isOaApproval" /> 关联 OA 移动审批</label>
|
||||
</div>
|
||||
<div class="trigger-condition-box">
|
||||
<div class="trigger-condition-title">
|
||||
<span>审批条件</span>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Text.Json;
|
||||
using YLErp.Helpers;
|
||||
using YLErp.Model;
|
||||
using YLErp.Modules.TradeModule;
|
||||
|
||||
namespace YLErp.Web.WebAPI.Controllers
|
||||
{
|
||||
/// <summary>国联民生衍生品移动审批轮询入口,由 PowerJob 等外部调度器调用。</summary>
|
||||
[ApiController]
|
||||
public class TradeApprovalOAJobController : ControllerBase
|
||||
{
|
||||
[HttpPost]
|
||||
[Route("api/job/tradeApproval/syncOAStatus")]
|
||||
public JsonResult SyncOAStatus([FromQuery] string requestId = null)
|
||||
{
|
||||
var configKey = AppManager.GetConfiguration()["JobConfig:ApiKey"];
|
||||
if (!string.IsNullOrEmpty(configKey) && Request.Headers["X-Job-Api-Key"].FirstOrDefault() != configKey)
|
||||
return new JsonResult(new { code = 401, msg = "Invalid API Key" });
|
||||
|
||||
var user = new OptUserInfo(0, "系统移动审批同步", OptUserFrom.System);
|
||||
var messages = new TradeApprovalOAService(user).SyncPendingStatuses(requestId);
|
||||
return new JsonResult(new { code = 0, data = new { total = messages.Count, details = messages } }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using YLErp.Helpers;
|
||||
|
||||
namespace YLErp.Web.WebAPI.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 仅供本地开发验证收益互换移动审批调用链的 OA 模拟接口。
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/mock/tradeApprovalOa")]
|
||||
public class TradeApprovalOaMockController : ControllerBase
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, MockOaFlow> Flows = new();
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
[HttpPost("createOaFlow")]
|
||||
public IActionResult CreateOaFlow([FromBody] JsonElement payload)
|
||||
{
|
||||
if (!IsEnabled()) return NotFound();
|
||||
|
||||
var requestId = "mock-" + Guid.NewGuid().ToString("N");
|
||||
Flows[requestId] = new MockOaFlow { RequestId = requestId };
|
||||
return Json(new { code = 0, data = new { msg = string.Empty, requestid = requestId }, status = "SUCCESS" });
|
||||
}
|
||||
|
||||
[HttpGet("getRequestData")]
|
||||
public IActionResult GetRequestData([FromQuery] string requestId)
|
||||
{
|
||||
if (!IsEnabled()) return NotFound();
|
||||
|
||||
if (!Flows.TryGetValue(requestId ?? string.Empty, out var flow))
|
||||
return Json(new { code = 0, data = Array.Empty<object>(), status = "SUCCESS" });
|
||||
|
||||
return Json(new
|
||||
{
|
||||
code = 0,
|
||||
data = new[] { new { requestId = flow.RequestId, flowStatusType = flow.FlowStatusType, status = flow.Status } },
|
||||
status = "SUCCESS"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("forceEndOaFlow")]
|
||||
public IActionResult ForceEndOaFlow([FromQuery] string requestId)
|
||||
{
|
||||
if (!IsEnabled()) return NotFound();
|
||||
|
||||
if (requestId != null && Flows.TryGetValue(requestId, out var flow))
|
||||
{
|
||||
flow.FlowStatusType = "3";
|
||||
flow.Status = "归档";
|
||||
flow.IsEnded = true;
|
||||
}
|
||||
|
||||
return Json(new { code = 0, data = new { resultcode = "0" }, status = "SUCCESS" });
|
||||
}
|
||||
|
||||
[HttpPost("setStatus")]
|
||||
public IActionResult SetStatus([FromQuery] string requestId, [FromQuery] string flowStatusType, [FromQuery] string status = null)
|
||||
{
|
||||
if (!IsEnabled()) return NotFound();
|
||||
if (flowStatusType != "0" && flowStatusType != "1" && flowStatusType != "2" && flowStatusType != "3")
|
||||
return BadRequest(new { code = 1, msg = "flowStatusType 只能为 0、1 或 2" });
|
||||
if (!Flows.TryGetValue(requestId ?? string.Empty, out var flow))
|
||||
return NotFound(new { code = 1, msg = "未找到 requestId" });
|
||||
if (flow.IsEnded)
|
||||
return BadRequest(new { code = 1, msg = "OA 流程已归档" });
|
||||
|
||||
flow.FlowStatusType = flowStatusType;
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
flow.Status = status;
|
||||
return Json(new { code = 0, data = new { requestId, flowStatusType, status = flow.Status }, status = "SUCCESS" });
|
||||
}
|
||||
|
||||
[HttpGet("flows")]
|
||||
public IActionResult GetFlows()
|
||||
{
|
||||
if (!IsEnabled()) return NotFound();
|
||||
|
||||
return Json(new
|
||||
{
|
||||
code = 0,
|
||||
data = Flows.Values.Select(x => new { requestId = x.RequestId, flowStatusType = x.FlowStatusType, status = x.Status, isEnded = x.IsEnded }),
|
||||
status = "SUCCESS"
|
||||
});
|
||||
}
|
||||
|
||||
private static bool IsEnabled()
|
||||
{
|
||||
return bool.TryParse(AppManager.GetConfiguration()["TradeApprovalOaMock:Enabled"], out var enabled) && enabled;
|
||||
}
|
||||
|
||||
private JsonResult Json(object value)
|
||||
{
|
||||
return new JsonResult(value, JsonOptions);
|
||||
}
|
||||
|
||||
private sealed class MockOaFlow
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
public string FlowStatusType { get; set; } = "0";
|
||||
public string Status { get; set; } = "创建";
|
||||
public bool IsEnded { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -78,13 +78,26 @@
|
||||
"BaseUrl": "http://10.250.202.40:13590",
|
||||
"SubUrl": "/gateway/oaflow/createOaFlow",
|
||||
"QueryUrl": "/gateway/oaflow/getRequestData",
|
||||
"TradeApprovalQueryUrl": "/gateway/oaflow/getRequestDataPro",
|
||||
"ForceEndUrl": "/gateway/oaflow/forceEndOaFlow",
|
||||
"MobileFlowId": 245103,
|
||||
"DownloadFileUrl": "/gateway/oaflow/downloadSingleFile",
|
||||
"SystemToken": "QIkox8sv3rVLtiT1wmI4Dp8wSjX9D84Glkp121vRino9fLobWnDiy8ppmqJcv1676TYuxZsGrYelx0whRCMaPyums2VnzIYrpqCFf5tyc3/x22Elf25xgRmd5pH8tLcnPuVOjMrUf0GsoytQCGFkhnrPajhsKq/mSwYQ4OA=", //OA系统key
|
||||
"SystemName": "onederiv",
|
||||
"IsNextFlow": 1, //是否自动提交 默认0
|
||||
"IsNextFlow": 1, // 交易确认书 OA 参数
|
||||
"TradeApprovalIsNextFlow": 0, // 收益互换移动审批 OA 参数
|
||||
"Creator": "guoj", // OA 流程创建人账号
|
||||
"Applicant": "guoj", // OA 流程申请人(sqr)账号
|
||||
"FlowId": 779115,
|
||||
"UploadUrl": "http://10.250.202.40:13590/gateway/oaflow/uploadDoc"
|
||||
},
|
||||
"TradeApprovalOaMock": {
|
||||
"Enabled": false,
|
||||
"BaseUrl": "http://localhost:49462",
|
||||
"SubUrl": "/api/mock/tradeApprovalOa/createOaFlow",
|
||||
"TradeApprovalQueryUrl": "/gateway/oaflow/getRequestDataPro",
|
||||
"ForceEndUrl": "/api/mock/tradeApprovalOa/forceEndOaFlow"
|
||||
},
|
||||
"GeneralSSO": {
|
||||
"enable": true, //是否启用通用SSO登录
|
||||
"auth_url": "http://192.168.2.96:10117/sso/login", //SSO授权地址
|
||||
|
||||
@@ -77,10 +77,21 @@
|
||||
"oa_confg": {
|
||||
"BaseUrl": "http://ip:port",
|
||||
"SubUrl": "",
|
||||
"TradeApprovalQueryUrl": "",
|
||||
"UploadUrl": "/api/oa/upload", // OA附件上传接口路径
|
||||
"SystemToken": "your-system-token", // OA系统鉴权Token
|
||||
"SystemName": "OTC_TRADE", // 调用方系统标识
|
||||
"IsNextFlow": 0
|
||||
"IsNextFlow": 1, // 交易确认书 OA 参数
|
||||
"TradeApprovalIsNextFlow": 0, // 收益互换移动审批 OA 参数
|
||||
"Creator": "", // OA 流程创建人账号
|
||||
"Applicant": "" // OA 流程申请人(sqr)账号
|
||||
},
|
||||
"TradeApprovalOaMock": {
|
||||
"Enabled": false,
|
||||
"BaseUrl": "http://localhost:49462",
|
||||
"SubUrl": "/api/mock/tradeApprovalOa/createOaFlow",
|
||||
"TradeApprovalQueryUrl": "/api/mock/tradeApprovalOa/getRequestData",
|
||||
"ForceEndUrl": "/api/mock/tradeApprovalOa/forceEndOaFlow"
|
||||
},
|
||||
"GeneralSSO": {
|
||||
"enable": true, //是否启用通用SSO登录
|
||||
|
||||
@@ -308,6 +308,7 @@ var app = new Vue({
|
||||
approvalGroupId: 0,
|
||||
conditionConfig: '',
|
||||
triggerCondition: '',
|
||||
isOaApproval: false,
|
||||
_trigger: { tokens: [] }
|
||||
}
|
||||
var item2 = {
|
||||
@@ -321,6 +322,7 @@ var app = new Vue({
|
||||
approvalGroupId: 0,
|
||||
conditionConfig: '',
|
||||
triggerCondition: '',
|
||||
isOaApproval: false,
|
||||
_trigger: { tokens: [] }
|
||||
}
|
||||
if (selectType == "2") { //交易
|
||||
@@ -469,6 +471,7 @@ var app = new Vue({
|
||||
approvalGroupId: 0,
|
||||
conditionConfig: '',
|
||||
triggerCondition: '',
|
||||
isOaApproval: false,
|
||||
_trigger: { tokens: [] }
|
||||
}
|
||||
var childNodes = thisObj.openItems.filter(x => x.Index > index);
|
||||
@@ -494,6 +497,7 @@ var app = new Vue({
|
||||
approvalGroupId: 0,
|
||||
conditionConfig: '',
|
||||
triggerCondition: '',
|
||||
isOaApproval: false,
|
||||
_trigger: { tokens: [] }
|
||||
}
|
||||
thisObj.getRuleType();
|
||||
@@ -956,7 +960,8 @@ var app = new Vue({
|
||||
approvalGroupId: value.approvalGroupId,
|
||||
approvalCondition: value.approvalCondition,
|
||||
conditionConfig: value.conditionConfig,
|
||||
triggerCondition: value.triggerCondition
|
||||
triggerCondition: value.triggerCondition,
|
||||
isOaApproval: value.isOaApproval === true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -972,7 +977,8 @@ var app = new Vue({
|
||||
approvalGroupId: value.approvalGroupId,
|
||||
approvalCondition: value.approvalCondition,
|
||||
conditionConfig: value.conditionConfig,
|
||||
triggerCondition: value.triggerCondition
|
||||
triggerCondition: value.triggerCondition,
|
||||
isOaApproval: value.isOaApproval === true
|
||||
});
|
||||
});
|
||||
// 需求②:了结/平仓/行权审批流程
|
||||
@@ -990,6 +996,7 @@ var app = new Vue({
|
||||
approvalCondition: value.approvalCondition,
|
||||
conditionConfig: value.conditionConfig,
|
||||
triggerCondition: value.triggerCondition
|
||||
,isOaApproval: value.isOaApproval === true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,6 +79,13 @@ var colModelGrid = [
|
||||
width: 60,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
name: 'OaRemark',
|
||||
label: 'OA备注',
|
||||
index: 'OaRemark',
|
||||
width: 80,
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
label: 'id',
|
||||
|
||||
Reference in New Issue
Block a user