440 lines
16 KiB
C#
440 lines
16 KiB
C#
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
using System.Reflection;
|
|
using YLErp.DBModels.Attributes;
|
|
|
|
namespace YLErp.Modules.TradeModule
|
|
{
|
|
/// <summary>
|
|
/// 交易变更比对处理
|
|
/// </summary>
|
|
public static class TradeChangeUtil
|
|
{
|
|
static readonly IEnumerable<PropInfo>[] _propInfoArr;
|
|
|
|
static TradeChangeUtil()
|
|
{
|
|
_propInfoArr = new IEnumerable<PropInfo>[20];
|
|
_propInfoArr[0] = GetPropInfos(typeof(trade));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易数据变更信息(包含扩展数据)
|
|
/// </summary>
|
|
public static List<TradeChangeInfo> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取基础交易数据变更信息(仅针对trade自身)
|
|
/// </summary>
|
|
public static List<TradeChangeInfo> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易数据变更信息(包含扩展数据)
|
|
/// </summary>
|
|
public static IEnumerable<TradeChangeInfo> 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<TradeChangeInfo>();
|
|
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<TradeChangeInfo> GetChanges(int index, object oldData, object newData)
|
|
{
|
|
var propInfos = _propInfoArr[index];
|
|
if (propInfos == null)
|
|
{
|
|
propInfos = GetPropInfos(oldData.GetType());
|
|
}
|
|
|
|
var changeList = new List<TradeChangeInfo>();
|
|
|
|
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<PropInfo> GetPropInfos(Type type)
|
|
{
|
|
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
|
var list = new List<PropInfo>(props.Length);
|
|
foreach (var p in props)
|
|
{
|
|
if (!p.CanWrite) continue;
|
|
var custAttrs = p.GetCustomAttributes();
|
|
var auditAttr = custAttrs.OfType<TradeAuditAttribute>().FirstOrDefault();
|
|
if (auditAttr != null && auditAttr.Flag == TradeAuditFlag.exclude) continue;
|
|
if (auditAttr != null || !custAttrs.OfType<NotMappedAttribute>().Any())
|
|
{
|
|
var displayAttr = custAttrs.OfType<DisplayNameAttribute>().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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 序列化变更信息
|
|
/// </summary>
|
|
public static string SerializeChanges(IEnumerable<TradeChangeInfo> changeInfos, out TradeAuditFlag flag)
|
|
{
|
|
flag = TradeAuditFlag.changed;
|
|
|
|
if (changeInfos == null || !changeInfos.Any()) return string.Empty;
|
|
|
|
var list = new List<string>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 反序列化变更信息
|
|
/// </summary>
|
|
public static IEnumerable<TradeChangeInfo> DeserializeChanges(string changInfo)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(changInfo))
|
|
return Array.Empty<TradeChangeInfo>();
|
|
|
|
var list = new List<TradeChangeInfo>();
|
|
var sr = new StringReader(changInfo);
|
|
while (sr.Peek() > 0)
|
|
{
|
|
var line = sr.ReadLine().Trim();
|
|
if (line.Length > 0)
|
|
{
|
|
var dataArr = JsonHelper.Deserialize<string[]>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 合并两个changes集合,实现同一个属性从最初值到最新值的合并
|
|
/// </summary>
|
|
public static IEnumerable<TradeChangeInfo> MergeChanges(IEnumerable<TradeChangeInfo> ltChangeInfos, IEnumerable<TradeChangeInfo> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static string GetChangeMsg(IEnumerable<TradeChangeInfo> 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<object, object> GetValue;
|
|
public TradeAuditFlag AuditFlag;
|
|
public int TypeFalg;//1:double,2:double?
|
|
public override string ToString()
|
|
{
|
|
return PropName + "--" + DisplayName;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 交易变更信息
|
|
/// </summary>
|
|
public class TradeChangeInfo
|
|
{
|
|
/// <summary>
|
|
/// 字段名称
|
|
/// </summary>
|
|
public string PropName { get; set; }
|
|
|
|
/// <summary>
|
|
/// 显示名称
|
|
/// </summary>
|
|
public string DisplayName { get; set; }
|
|
|
|
/// <summary>
|
|
/// 新值
|
|
/// </summary>
|
|
public string NewValue { get; set; }
|
|
|
|
/// <summary>
|
|
/// 旧值
|
|
/// </summary>
|
|
public string OldValue { get; set; }
|
|
|
|
/// <summary>
|
|
/// 审查选项
|
|
/// </summary>
|
|
public TradeAuditFlag AuditFlag { get; set; }
|
|
|
|
public bool IsChangeMsg => (AuditFlag & TradeAuditFlag.changed) == TradeAuditFlag.changMsg;
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{PropName}--{DisplayName}--new:{NewValue},old:{OldValue}";
|
|
}
|
|
}
|
|
}
|