using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; using YLErp.DBModels.Attributes; namespace YLErp.Modules.TradeModule { /// /// 交易变更比对处理 /// public static class TradeChangeUtil { static readonly IEnumerable[] _propInfoArr; static TradeChangeUtil() { _propInfoArr = new IEnumerable[20]; _propInfoArr[0] = GetPropInfos(typeof(trade)); } /// /// 获取交易数据变更信息(包含扩展数据) /// public static List GetAllChanges(trade oldData, trade newData) { if (oldData is null) { throw new ArgumentNullException(nameof(oldData)); } if (newData is null) { throw new ArgumentNullException(nameof(newData)); } var changeList = GetChanges(0, oldData, newData); var changeListExt = GetExtendChanges(oldData, newData); changeList.AddRange(changeListExt); return changeList; } /// /// 获取基础交易数据变更信息(仅针对trade自身) /// public static List GetBasicChanges(trade oldData, trade newData) { if (oldData is null) { throw new ArgumentNullException(nameof(oldData)); } if (newData is null) { throw new ArgumentNullException(nameof(newData)); } return GetChanges(0, oldData, newData); } /// /// 获取交易数据变更信息(包含扩展数据) /// public static IEnumerable GetExtendChanges(trade oldData, trade newData) { if (oldData is null) { throw new ArgumentNullException(nameof(oldData)); } if (newData is null) { throw new ArgumentNullException(nameof(newData)); } //交易类型一致时才比较扩展数据 if (newData.TradeType == oldData.TradeType) { var index = 0; object oldDataExt = null, newDataExt = null; switch (oldData.TradeType) { case "香草期权": return Enumerable.Empty(); case "障碍期权": index = 1; oldDataExt = oldData.trade_barrier_option; newDataExt = newData.trade_barrier_option; break; case "二元期权": index = 2; oldDataExt = oldData.trade_binary_option; newDataExt = newData.trade_binary_option; break; case "亚式期权": index = 3; oldDataExt = oldData.trade_asian_option; newDataExt = newData.trade_asian_option; break; case "彩虹期权": index = 4; oldDataExt = oldData.trade_rainbow_option; newDataExt = newData.trade_rainbow_option; break; case "价差期权": index = 5; oldDataExt = oldData.trade_spread_option; newDataExt = newData.trade_spread_option; break; case "合成价差期权": index = 6; break; case "双鲨期权": index = 7; oldDataExt = oldData.trade_double_sharkfin_option; newDataExt = newData.trade_double_sharkfin_option; break; case "区间累积期权": index = 8; oldDataExt = oldData.trade_rangeaccrual; newDataExt = newData.trade_rangeaccrual; break; case "收益增强结构": index = 9; oldDataExt = oldData.trade_underlying_enhance; newDataExt = newData.trade_underlying_enhance; break; case "气囊结构": index = 10; oldDataExt = oldData.trade_airbag; newDataExt = newData.trade_airbag; break; case "凤凰期权": index = 11; oldDataExt = oldData.trade_autocall; newDataExt = newData.trade_autocall; break; case "雪球期权": index = 12; oldDataExt = oldData.trade_snowball; newDataExt = newData.trade_snowball; break; case "远期": index = 13; oldDataExt = oldData.trade_forward; newDataExt = newData.trade_forward; break; case "收益互换": index = 14; oldDataExt = oldData.trade_swap; newDataExt = newData.trade_swap; break; case "现金流交易": index = 15; oldDataExt = oldData.trade_cashflow; newDataExt = newData.trade_cashflow; break; case "Risky期权": index = 16; oldDataExt = oldData.trade_risky_option; newDataExt = newData.trade_risky_option; break; } if (oldDataExt != null && newDataExt != null) { return GetChanges(index, oldDataExt, newDataExt); } return new TradeChangeInfo[] { new TradeChangeInfo { PropName = string.Empty, DisplayName = $"扩展数据({oldData.TradeType})", NewValue = newDataExt == null ? "不存在" : "存在", OldValue = newDataExt == null ? "不存在" : "存在" } }; } return new TradeChangeInfo[] { new TradeChangeInfo { PropName = string.Empty, DisplayName ="扩展数据", NewValue = newData.TradeType, OldValue = oldData.TradeType } }; } //比较旧数据和新数据并返回更改 private static List GetChanges(int index, object oldData, object newData) { var propInfos = _propInfoArr[index]; if (propInfos == null) { propInfos = GetPropInfos(oldData.GetType()); } var changeList = new List(); foreach (var p in propInfos) { double GetMin(string so, string sn) { var min = 1e-5; var mstr = so.Length < sn.Length ? so : sn; var pow = mstr.Length - mstr.IndexOf('.') - 1; if (pow < mstr.Length && pow > 5) { min = 0.55 / Math.Pow(10, pow); } return min; } var xold = p.GetValue(oldData); var xnew = p.GetValue(newData); var sold = (xold ?? string.Empty).ToString().Trim(); var snew = (xnew ?? string.Empty).ToString().Trim(); if (sold != snew) { if (p.TypeFalg > 0 && sold.Length != snew.Length) { if (p.TypeFalg == 1) { var min = GetMin(sold, snew); if (Math.Abs((double)xold - (double)xnew) < min) continue; } else if (p.TypeFalg == 2) { var x = ((double?)xold) ?? 0; var y = ((double?)xnew) ?? 0; var min = GetMin(sold, snew); if (Math.Abs(x - y) < min) continue; } } changeList.Add(new TradeChangeInfo { NewValue = snew, OldValue = sold, PropName = p.PropName, AuditFlag = p.AuditFlag, DisplayName = p.DisplayName }); } } return changeList; } //获取反射的属性数据 private static IEnumerable GetPropInfos(Type type) { var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); var list = new List(props.Length); foreach (var p in props) { if (!p.CanWrite) continue; var custAttrs = p.GetCustomAttributes(); var auditAttr = custAttrs.OfType().FirstOrDefault(); if (auditAttr != null && auditAttr.Flag == TradeAuditFlag.exclude) continue; if (auditAttr != null || !custAttrs.OfType().Any()) { var displayAttr = custAttrs.OfType().FirstOrDefault(); var propInfo = new PropInfo { PropName = p.Name, GetValue = p.GetValue, AuditFlag = auditAttr != null ? auditAttr.Flag : TradeAuditFlag.changed, DisplayName = auditAttr != null ? auditAttr.DisplayName : displayAttr != null ? displayAttr.DisplayName : p.Name }; if (p.PropertyType == typeof(double)) { propInfo.TypeFalg = 1; } else if (p.PropertyType == typeof(double?)) { propInfo.TypeFalg = 2; } list.Add(propInfo); } } list.TrimExcess(); return list; } /// /// 序列化变更信息 /// public static string SerializeChanges(IEnumerable changeInfos, out TradeAuditFlag flag) { flag = TradeAuditFlag.changed; if (changeInfos == null || !changeInfos.Any()) return string.Empty; var list = new List(changeInfos.Count()); foreach (var ci in changeInfos) { if (ci == null) continue; var arr = new[] { ci.PropName, ci.DisplayName, ci.NewValue, ci.OldValue, ci.IsChangeMsg ? "1" : "0" }; var json = JsonHelper.Serialize(arr); list.Add(json); flag |= ci.AuditFlag; } return string.Join(Environment.NewLine, list); } /// /// 反序列化变更信息 /// public static IEnumerable DeserializeChanges(string changInfo) { if (string.IsNullOrWhiteSpace(changInfo)) return Array.Empty(); var list = new List(); var sr = new StringReader(changInfo); while (sr.Peek() > 0) { var line = sr.ReadLine().Trim(); if (line.Length > 0) { var dataArr = JsonHelper.Deserialize(line); var changeInfo = new TradeChangeInfo { PropName = dataArr[0], DisplayName = dataArr[1], NewValue = dataArr[2], OldValue = dataArr[3] }; list.Add(changeInfo); if (dataArr.Length > 4) { changeInfo.AuditFlag = dataArr[4] == "1" ? TradeAuditFlag.changMsg : TradeAuditFlag.changed; } } } return list; } /// /// 合并两个changes集合,实现同一个属性从最初值到最新值的合并 /// public static IEnumerable MergeChanges(IEnumerable ltChangeInfos, IEnumerable rtChangeInfos) { if (ltChangeInfos is null) { return rtChangeInfos; } if (rtChangeInfos is null) { return ltChangeInfos; } var dic = ltChangeInfos.ToDictionary(n => n.PropName); foreach (var item in rtChangeInfos) { if (dic.TryGetValue(item.PropName, out var change)) { change.NewValue = item.NewValue; } else { dic[item.PropName] = item; } } return dic.Values; } /// /// /// public static string GetChangeMsg(IEnumerable changes) { if (changes is null || !changes.Any()) { return string.Empty; } return string.Join(string.Empty, changes.Select(n => string.Concat(n.DisplayName, "由", n.OldValue, "变更为", n.NewValue, ";\r\n"))); } class PropInfo { public string PropName; public string DisplayName; public Func GetValue; public TradeAuditFlag AuditFlag; public int TypeFalg;//1:double,2:double? public override string ToString() { return PropName + "--" + DisplayName; } } } /// /// 交易变更信息 /// public class TradeChangeInfo { /// /// 字段名称 /// public string PropName { get; set; } /// /// 显示名称 /// public string DisplayName { get; set; } /// /// 新值 /// public string NewValue { get; set; } /// /// 旧值 /// public string OldValue { get; set; } /// /// 审查选项 /// public TradeAuditFlag AuditFlag { get; set; } public bool IsChangeMsg => (AuditFlag & TradeAuditFlag.changed) == TradeAuditFlag.changMsg; public override string ToString() { return $"{PropName}--{DisplayName}--new:{NewValue},old:{OldValue}"; } } }