Files
zszq-trs/YLErpDAL/Modules/EodModule/SettlementModule/EodTaskSaveService.cs
T
2024-05-09 14:06:26 +08:00

473 lines
17 KiB
C#

using YLErp.Configuration;
using YLErp.Modules.SystemModule;
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// EodTask数据保存服务
/// </summary>
public class EodTaskSaveService : YLBaseService
{
public EodTaskSaveService(OptUserInfo optUser) : base(optUser)
{
}
public EodTaskSaveService(YLBaseService baseService) : base(baseService)
{
}
public EodTask CancelTask(int taskId)
{
if (EodTaskRunner.Stop(taskId))
{
return EodTaskRunner.LastTask;
}
var task = DbContext.EodTask.FirstOrDefault(n => n.id == taskId);
if (task == null)
{
throw new ServiceException("任务数据已不存在");
}
if (task.TaskState >= EodTaskState.completed)
{
throw new ServiceException("当前任务状态:" + EodTaskHelper.GetEodTaskStateDesc(task.TaskState));
}
task.OptId = UserId;
task.OptName = UserName;
task.OptTime = DateTime.Now;
task.TaskState = EodTaskState.canceled;
DbContext.SaveChanges();
return task;
}
public EodTask RestartTask(int taskId, bool resetAll, bool isDelayTrade)
{
var task = DbContext.EodTask.FirstOrDefault(n => n.id == taskId);
if (task == null)
{
throw new ServiceException("任务数据已不存在");
}
switch (task.TaskState)
{
case EodTaskState.completed:
case EodTaskState.failure:
case EodTaskState.canceled:
case EodTaskState.interrupted: break;
case EodTaskState.running:
if (EodTaskRunner.LastTask?.id == taskId)
{
throw new ServiceException("任务正在运行中");
}
break;
default: throw new ServiceException("不符合重新执行的条件,任务当前状态:" + EodTaskHelper.GetEodTaskStateDesc(task.TaskState));
}
task.OptId = UserId;
task.OptName = UserName;
task.OptTime = DateTime.Now;
task.TaskResult = string.Empty;
task.TaskState = EodTaskState.pending;
task.IsDelayTrade = isDelayTrade;
if (resetAll)
{
task.ValueDate = task.StartDate;
DbContext.EodTaskDetail.RemoveRange(DbContext.EodTaskDetail.Where(n => n.TaskId == taskId));
}
DbContext.SaveChanges();
return task;
}
/// <summary>
/// 保存结算任务
/// </summary>
public EodTask SaveData(EodTaskSaveReq req, bool saveIntoDB)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
var task = new EodTask
{
EndDate = req.EndDate,
StartDate = req.StartDate,
TaskEndTime = DateTime.Now,
TaskStartTime = DateTime.Now,
TaskResult = string.Empty,
TaskState = EodTaskState.pending,
PartFlag = req.PartFlag,
ValueDate = req.StartDate,
PriceType = req.PriceType,
VolTypes = req.VolTypes,
IsDelayTrade = req.IsDelayTrade,
CreatorId = UserId,
CreatorName = UserName,
CreateTime = DateTime.Now,
OptId = UserId,
OptName = UserName,
OptTime = DateTime.Now,
};
if (req.ClientIds != null)
{
task.ClientIds = String.Join(",", req.ClientIds);
task.ListClientId = req.ClientIds;
task.ClientNameStr = String.Join(",", ClientDataModelV1.GetAllOpenClient(isContainClosed: true).Where(l => task.ListClientId.Contains(int.Parse(l.Value))).Select(l => l.Text));
}
var result = CheckEodTask(task);
if (result.HasError)
{
throw new ServiceException(result.ErrorMsg);
}
task.VolTypes = string.Join(",", result.VolTypes);
if (saveIntoDB)
{
DbContext.EodTask.Add(task);
DbContext.SaveChanges();
}
return task;
}
internal CheckPreEodStatusResult CheckEodTask(EodTask req)
{
var result = new CheckPreEodStatusResult();
if (req.StartDate.Year < 2000)
{
return result.SetError(true, "收盘开始日期 必须填写");
}
if (req.EndDate == DateTime.MinValue)
{
req.EndDate = req.StartDate;
}
else if (req.EndDate.Year < 2000)
{
return result.SetError(true, "收盘结束日期 必须填写");
}
if (req.EndDate < req.StartDate)
{
return result.SetError(true, "收盘结束日期 必须大于或等于 收盘开始日期");
}
if (string.IsNullOrWhiteSpace(req.VolTypes))
{
return result.SetError(true, "波动率类型 必须填写");
}
if (req.PriceType < EodSettlePriceType.ClosePrice || req.PriceType > EodSettlePriceType.AllPrices)
{
return result.SetError(true, "价格模式 无效值");
}
if (!EodTaskHelper.IsValidPartFlag(req.PartFlag))
{
return result.SetError(true, "部分结算 无效值");
}
var valueDate = BLL.valuedateBLL.GetNonHoliday(req.StartDate);
if (valueDate > req.EndDate)
{
return result.SetError(true, "收盘开始日期至收盘结束日期没有交易日");
}
//波动率结算是否满足条件
var volTypes = req.VolTypes.Split(',').Where(n => !string.IsNullOrWhiteSpace(n)).ToArray();
if (!volTypes.Any())
{
return result.SetError(true, "波动率类型 必须填写");
}
var volTypeFlag = VolTypeFlagEnum.None;
var volTypeFlagList = new List<VolTypeFlagEnum>(6);
foreach (var voltype in volTypes)
{
var tempVolType = VolTypeFlagEnumHelper.GetVolTypeFlag(voltype);
if (tempVolType == VolTypeFlagEnum.None && !string.IsNullOrWhiteSpace(voltype))
{
return result.SetError(true, "不能辨认的波动率类型:" + voltype);
}
volTypeFlag |= tempVolType;
volTypeFlagList.Add(tempVolType);
}
if (volTypeFlag == VolTypeFlagEnum.None)
{
return result.SetError(true, "波动率类型 必须有值");
}
var curEodStatus = new Lazy<EodStatus>(() =>
{
using (var db = DbContextFactory.GetYLDbContext())
{
return DbContext.eodStatus.FirstOrDefault(n => n.ValueDate == valueDate);
}
});
if (!volTypeFlag.HasFlag(VolTypeFlagEnum.PositionVol))
{
if (req.UseClosePrice() && (curEodStatus.Value == null || !curEodStatus.Value.VolTypeFlag.HasFlag(VolTypeFlagEnum.PositionVol)))
{
return result.SetError(false, $"结算日'{valueDate:yyyy-MM-dd}'需执行'收盘价+持仓波动率'结算");
}
if (req.UseSettlePrice() && (curEodStatus.Value == null || !curEodStatus.Value.VolTypeFlag_S.HasFlag(VolTypeFlagEnum.PositionVol)))
{
return result.SetError(false, $"结算日'{valueDate:yyyy-MM-dd}'需执行'结算价+持仓波动率'结算");
}
}
//按优先级重新排序
result.VolTypes = volTypeFlagList.OrderBy(n => n).Select(n => VolTypeFlagEnumHelper.GetSingleDesc(n)).ToArray();
//部分结算是否满足条件
if (req.PartFlag == "场外" || req.PartFlag == "场内")
{
if (req.UseClosePrice())
{
var missingVolTypes = GetMissingVolTypes(curEodStatus.Value?.VolTypeFlag ?? VolTypeFlagEnum.None, volTypeFlag);
if (!string.IsNullOrEmpty(missingVolTypes))
{
return result.SetError(false, $"结算日'{valueDate:yyyy-MM-dd}'不能进行{req.PartFlag}部分结算,因未完成'收盘价+{missingVolTypes}波动率'的完整结算");
}
}
if (req.UseSettlePrice())
{
var missingVolTypes = GetMissingVolTypes(curEodStatus.Value?.VolTypeFlag_S ?? VolTypeFlagEnum.None, volTypeFlag);
if (!string.IsNullOrEmpty(missingVolTypes))
{
return result.SetError(false, $"结算日'{valueDate:yyyy-MM-dd}'不能进行{req.PartFlag}部分结算,因未完成'结算价+{missingVolTypes}波动率'的完整结算");
}
}
}
//上一交易日是否满足条件
var preDate = BLL.valuedateBLL.GetNonHolidayDefore(valueDate.AddDays(-1));
var preEodStatus = DbContext.eodStatus.AsNoTracking().Where(n => n.ValueDate < valueDate && n.Status == "已收盘")
.OrderByDescending(n => n.ValueDate).FirstOrDefault();
if (preEodStatus != null && string.IsNullOrEmpty(req.ClientIds))
{
if (preEodStatus.ValueDate < preDate)
{
return result.SetError(false, $"上一交易日'{preDate:yyyy-MM-dd}'未进行收盘,必须在上一交易日收盘后方能执行此次收盘任务");
}
if (preEodStatus.ValueDate > preDate)
{
return result.SetError(false, $"是否存在日历变更?数据库中已执行的上一收盘日'{preEodStatus.ValueDate:yyyy-MM-dd}'并非交易日,请将开始日期调整为'{preDate:yyyy-MM-dd}'");
}
//收盘价
if (req.UseClosePrice() && !preEodStatus.VolTypeFlag.HasFlag(volTypeFlag))
{
var list = new List<string>();
for (var i = 0; i < volTypeFlagList.Count; i++)
{
if (volTypeFlagList[i] != (volTypeFlagList[i] & preEodStatus.VolTypeFlag))
{
list.Add(volTypes[i]);
}
}
var str = string.Join(",", list);
return result.SetError(false, $"上一交易日'{preDate:yyyy-MM-dd}'未进行'收盘价+波动率({str})'收盘,必须在上一交易日收盘后方能执行此次收盘任务");
}
//结算价
if (req.UseSettlePrice() && !preEodStatus.VolTypeFlag_S.HasFlag(volTypeFlag))
{
var list = new List<string>();
for (var i = 0; i < volTypeFlagList.Count; i++)
{
if (volTypeFlagList[i] != (volTypeFlagList[i] & preEodStatus.VolTypeFlag_S))
{
list.Add(volTypes[i]);
}
}
var str = string.Join(",", list);
return result.SetError(false, $"上一交易日'{preDate:yyyy-MM-dd}'未进行'结算价+波动率({str})'收盘,必须在上一交易日收盘后方能执行此次收盘任务");
}
}
//分客户收盘
if (!string.IsNullOrEmpty(req.ClientIds))
{//分客户收盘时通过ClientBalanceDaily表确定该客户前一天是否收盘;
var clientIds = req.ListClientId;
var eodClientIds = DbContext.ClientBalanceDaily.Where(O => O.BalanceDate == preDate && clientIds.Contains(O.ClientId)).Select(O => O.ClientId).ToList();
clientIds = clientIds.Except(eodClientIds).ToList();
if (clientIds.Any())
{//忽略掉收盘日之前没有出入金记录,授信记录和新交易的客户;
var validClientIds =
DbContext.credit
.Where(O => clientIds.Contains(O.ClientId ?? 0) && O.CreditStartDate < valueDate).Select(O => O.ClientId ?? 0)
.Union(
DbContext.ClientCashInCashOut
.Where(O => clientIds.Contains(O.ClientId ?? 0) && O.HappenDate < valueDate && O.ValidState != "InValid").Select(O => O.ClientId ?? 0))
.Union(
DbContext.trade.Where(O => clientIds.Contains(O.ClientId) && O.TradeDate < valueDate && O.ValidState != "InValid").Select(O => O.ClientId))
.Distinct().ToArray();
if (validClientIds.Any())
{
var clientNameList = validClientIds.Select(O => DataCacheProvider.GetClientDataSource().GetData(O).Name).ToList();
return result.SetError(false, $"上一交易日'{preDate:yyyy-MM-dd}' 客户:{string.Join(",", clientNameList)} 未进行收盘,必须在上一交易日收盘后方能执行此次收盘任务");
}
}
}
return result;
}
private string GetMissingVolTypes(VolTypeFlagEnum existing, VolTypeFlagEnum expected)
{
if ((existing & expected) == expected)
{
return string.Empty;
}
var list = new List<string>();
if (expected.HasFlag(VolTypeFlagEnum.PositionVol) && !existing.HasFlag(VolTypeFlagEnum.PositionVol))
{
list.Add("持仓");
}
if (expected.HasFlag(VolTypeFlagEnum.HedgeVol) && !existing.HasFlag(VolTypeFlagEnum.HedgeVol))
{
list.Add("对冲");
}
if (expected.HasFlag(VolTypeFlagEnum.OpenVol) && !existing.HasFlag(VolTypeFlagEnum.OpenVol))
{
list.Add("开仓");
}
if (expected.HasFlag(VolTypeFlagEnum.ExtendVol) && !existing.HasFlag(VolTypeFlagEnum.ExtendVol))
{
if (PS.Config.Company == CompanyEnum.光大光子)
{
list.Add("光证");
}
else if (PS.Config.Is格林大华 || PS.Config.Is伴兴)
{
list.Add("BidAskVol");
}
else
{
list.Add("未知");
}
}
return string.Join(",", list);
}
}
/// <summary>
///
/// </summary>
public class EodTaskSaveReq
{
/// <summary>
/// 收盘开始日期
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// 收盘结束日期
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// 波动率类型
/// </summary>
public string VolTypes { get; set; }
/// <summary>
/// 分红率类型 交易、0 国元特有
/// </summary>
public string DividendRateTypes { get; set; }
/// <summary>
/// 1=收盘价,2=结算价,3=收盘价+结算价
/// </summary>
public EodSettlePriceType PriceType { get; set; }
/// <summary>
/// 客户
/// </summary>
public List<int> ClientIds { get; set; }
/// <summary>
/// 部分收盘标识
/// </summary>
public string PartFlag { get; set; }
/// <summary>
/// 是否延期交易
/// </summary>
public bool IsDelayTrade { get; set; }
}
/// <summary>
///
/// </summary>
class CheckPreEodStatusResult
{
/// <summary>
///
/// </summary>
public bool HasError { get; set; }
/// <summary>
///
/// </summary>
public bool IsFatalError { get; set; }
/// <summary>
///
/// </summary>
public string ErrorMsg { get; set; }
/// <summary>
/// 根据优先级重新排序后的波动率集合
/// </summary>
public string[] VolTypes { get; set; }
public CheckPreEodStatusResult SetError(bool isFatalError, string errorMsg)
{
HasError = true;
ErrorMsg = errorMsg;
IsFatalError = isFatalError;
return this;
}
}
}