Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapFlowImportService.cs
T
hjhan 782e1f6a7a refactor(swap): 债券价格÷100转换收敛到BondPriceConverter统一入口
消除9处价格转换字面量(*0.01m/*100),统一走BondPriceConverter.ToStorage/ToDisplay。
- 新增YLErpDAL/Helpers/BondPriceConverter.cs(含可空重载)
- 批次1(危险字面量): BondPaymentService/RealtimePnlCalc/RealTimeClientBanlanceService/SwapTradeAutoService
- 批次2-4(调用收敛): SwapFlowService/SwapFlowImportService/SwapEndConfirmService/EodPriceProvider/EodPriceQueryService
- RealtimePnlCalc:601/602价格×数量维度交织处加注释,不机械合并

不改bondPriceMultiple常量值,纯调用方式收敛。SwapModule 155测试全绿,行为不变。
2026-07-03 08:22:27 +08:00

339 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DotNetDBF;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using YLErp.Helpers;
using YLErp.DBModels;
using Org.BouncyCastle.Ocsp;
using YLErp.BLL.Eod;
using CsvHelper;
using YLErp.BLL;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// 互换成交流水服务
/// </summary>
public class SwapFlowImportService : YLBaseService
{
public SwapFlowImportService(OptUserInfo optUser) : base(optUser)
{
}
public SwapFlowImportService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 成交流水导入 dbf
/// </summary>
/// <param name="filePath">压缩包文件路径</param>
/// <param name="fileDir">压缩包解压路径</param>
/// <param name="totalNum">总条数</param>
/// <param name="successNum">成功条数</param>
[Obsolete]
public void ImportSwapTradesFromZip(string filePath, string fileDir, out int totalNum, out int successNum)
{
totalNum = 0;
successNum = 0;
var fileDirPath = ZipHelper.unZipFile(filePath, fileDir, out var msg);
var dbfFiles = GetDbfFiles(fileDirPath);
var swapFlowList = GetDbfDatas(dbfFiles);
if (swapFlowList.Count > 0)
{
DbContext.swap_flow.AddRange(swapFlowList);
DbContext.SaveChanges();
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 11, "互换流水导入完成");
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
}
}
/// <summary>
/// 成交流水导入 excel
/// </summary>
/// <param name="streamIn">上传的文件</param>
/// <param name="totalNum">总条数</param>
/// <param name="successNum">成功条数</param>
public void ImportSwapTradesFromExcel(Stream streamIn, out int totalNum, out int successNum)
{
totalNum = 0;
successNum = 0;
var rowIndex = 0;
try
{
var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0);
if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2)
{
throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" };
}
var table = ds.Tables[0];
var reader = new DataRowReaderHelper(table);
rowIndex = 1;
totalNum = table.Rows.Count - rowIndex;
Dictionary<long, List<string>> clientUmsDic = new Dictionary<long, List<string>>();
foreach (var row in table.Rows.Cast<DataRow>().Skip(rowIndex))
{
rowIndex++;
if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString())))
{
totalNum--;
continue;
}
reader.SetDataRow(row,rowIndex);
var swap_flow = new swap_flow();
swap_flow.FundAccount = reader.GetString("资金账号");
var uPattern = "^[0 - 9]{6,20}$";
Regex regex = new Regex(uPattern);
if (!string.IsNullOrEmpty(swap_flow.FundAccount)&&regex.IsMatch(swap_flow.FundAccount))
{
throw new ServiceException($"第{rowIndex}行资金账号应为20位以内的数字");
}
swap_flow.UnderlyingCode = reader.GetString("标的代码", true);
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(swap_flow.UnderlyingCode);
swap_flow.UnderlyingName = underlying?.UnderlyingName;
var clientName = reader.GetString("交易对手方", true);
swap_flow.OccurTime = reader.GetDate("交易日期", true);
swap_flow.SettleDate = valuedateBLL.GetNonHoliday(swap_flow.OccurTime.Value).Date;
swap_flow.BsType = reader.GetString("买卖方向", true) == "B" ? (int)EnumDirection.Long : (int)EnumDirection.Short;
swap_flow.TradingAmount = reader.GetDecimal("成交金额") ?? 0;
swap_flow.TradingFee = reader.GetDecimal("交易费用") ?? 0;
swap_flow.TradingQty = reader.GetDecimal("成交数量") ?? 0;
if (swap_flow.TradingQty<=0)
{
throw new ServiceException($"第{rowIndex}行成交数量必须大于0");
}
swap_flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
swap_flow.ContractSize= reader.GetDecimal("乘数") ?? 0;
swap_flow.TradingAmountAvg = reader.GetDecimal("成交全价") ?? 0;
swap_flow.TradingAmountFeeAvg = TradeFeeHelper.CalcPriceWithFee(swap_flow.TradingFee, swap_flow.TradingAmountAvg, swap_flow.TradingQty, swap_flow.BsType);
swap_flow.ytm = reader.GetDecimalOrPercent("成交收益率",false,true) ?? 0;
swap_flow.TradingAmountNet = reader.GetDecimal("成交净价") ?? 0;
swap_flow.TradingAmountNetFee = TradeFeeHelper.CalcPriceWithFee(swap_flow.TradingFee, swap_flow.TradingAmountNet??0, swap_flow.TradingQty, swap_flow.BsType);
if (underlying != null && underlying.IsBond())
{
// 成交流水债券报价(×100形式)转入库小数(×0.01),统一走 BondPriceConverter
swap_flow.TradingAmountAvg = BondPriceConverter.ToStorage(swap_flow.TradingAmountAvg);
swap_flow.TradingAmountFeeAvg = BondPriceConverter.ToStorage(swap_flow.TradingAmountFeeAvg);
// TradingAmountNet/NetFee 可空,null 时保持 null 语义(与原 *= 一致)
if (swap_flow.TradingAmountNet.HasValue)
swap_flow.TradingAmountNet = BondPriceConverter.ToStorage(swap_flow.TradingAmountNet.Value);
if (swap_flow.TradingAmountNetFee.HasValue)
swap_flow.TradingAmountNetFee = BondPriceConverter.ToStorage(swap_flow.TradingAmountNetFee.Value);
}
if (!string.IsNullOrEmpty(clientName))
{
var client = DataCacheProvider.GetClientDataSource().AsQueryable(x=>x.Name== clientName).FirstOrDefault();
swap_flow.ClientId = client?.id;
swap_flow.ClientName = clientName;
}
swap_flow.OptId = UserId;
swap_flow.OptName = UserName;
swap_flow.OptTime = DateTime.Now;
DbContext.swap_flow.Add(swap_flow);
if (!clientUmsDic.ContainsKey(swap_flow.ClientId ?? 0))
{
clientUmsDic.Add(swap_flow.ClientId ?? 0, new List<string> { swap_flow.UnderlyingCode });
}
else
{
if (!clientUmsDic[swap_flow.ClientId ?? 0].Contains(swap_flow.UnderlyingCode))
{
clientUmsDic[swap_flow.ClientId ?? 0].Add(swap_flow.UnderlyingCode);
}
}
successNum++;
}
DbContext.SaveChanges();
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
new RiskCacheService().refreshRiskCache(clientUmsDic);
}
catch (Exception ex)
{
LogFactory.GetLogger("导入互换成交流水").Error(ex);
throw new ServiceException($"第{rowIndex}行,发生错误:{ex.Message}", ex);
}
}
/// <summary>
/// 获取解压路径下所有符合的dbf文件
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private List<string> GetDbfFiles(string filePath)
{
List<string> files = new List<string>();
var allFiles = Directory.GetFiles(filePath, ".", SearchOption.AllDirectories);
foreach (var file in allFiles)
{
FileInfo _file = new FileInfo(file);
var fileName = _file.Name.ToLower();
//SZ:SJSMX20518.DBF SH:jsmx03_jsx73.518
if ((fileName.StartsWith("sjsmx") && fileName.Contains(".dbf")) || fileName.StartsWith("jsmx"))
{
files.Add(file);
}
}
return files;
}
/// <summary>
/// dbf文件中获取流水数据
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
private List<swap_flow> GetDbfDatas(List<string> files)
{
List<swap_flow> swap_Flows = new List<swap_flow>();
for (var i = 0; i < files.Count; i++)
{
using (var dbf = new DBFReader(files[i]))
{
var columnCount = dbf.Fields.Length;
if (columnCount != 48)
{
continue;
}
bool dbffile = dbf.Fields[0].Name == "MXJSZH";
var swapflows = new List<swap_flow>();
if (dbffile)//SZ
{
swapflows = GetSwapFlowData_SZ(dbf);
}
else //SH
{
swapflows = GetSwapFlowData_SH(dbf);
}
swap_Flows.AddRange(swapflows);
}
}
return swap_Flows;
}
/// <summary>
/// 获取SZ流水数据
/// </summary>
/// <param name="dbf"></param>
/// <returns></returns>
private List<swap_flow> GetSwapFlowData_SZ(DBFReader dbf)
{
List<swap_flow> swap_Flows = new List<swap_flow>();
var count = dbf.RecordCount;
for (var j = 0; j < count; j++)
{
var dbfRecord = dbf.NextRecord();
if (dbfRecord != null)
{
string MXZQLB = dbfRecord[18].ToString().Trim();//证券类别
if (MXZQLB != "00")//A股
{
continue;
}
decimal.TryParse(dbfRecord[12].ToString().Trim(), out decimal tradingQty);//成交数量(MXCJSL)
decimal.TryParse(dbfRecord[23].ToString().Trim(), out decimal tradingAmount);//清算本金(MXQSBJ)
decimal.TryParse(dbfRecord[33].ToString().Trim(), out decimal tradingFee);//收付净额(MXSXF)
var underlyingCode = dbfRecord[4].ToString().Trim() + ".SZ";// 证券代码(MXSFJE)
var date = dbfRecord[34].ToString().Trim();//成交日期MXCJRQ
var bstype = dbfRecord[17].ToString().Trim();//平仓标识(MXPCBS)
var bsType = 0;
if (bstype == "2" || bstype == "3")//2平仓 3强制平仓
{
bsType = 2;
}
else if (bstype == "1")//开仓
{
bsType = 1;
}
var tradeDate = ConvertDateTime(date);
swap_Flows.Add(new swap_flow()
{
FundAccount = dbfRecord[7].ToString().Trim(),//证券账户号码(MXZQZH)
OccurTime = tradeDate,
UnderlyingCode = underlyingCode,
BsType = bsType,
TradingQty = tradingQty,
TradingAmount = tradingAmount,
TradingFee = tradingFee - tradingAmount,//收付净额-清算本金
DataState = 1
});
}
}
return swap_Flows;
}
/// <summary>
/// 获取SH流水数据
/// </summary>
/// <param name="fields"></param>
/// <returns></returns>
private List<swap_flow> GetSwapFlowData_SH(DBFReader dbf)
{
List<swap_flow> swap_Flows = new List<swap_flow>();
var count = dbf.RecordCount;
for (var j = 0; j < count; j++)
{
var dbfRecord = dbf.NextRecord();
if (dbfRecord != null)
{
string SCDM = dbfRecord[0].ToString().Trim();//市场代码
if (SCDM != "01")//A股
{
continue;
}
var bstype = dbfRecord[29].ToString().Trim();//买卖标识(MMBZ)
decimal.TryParse(dbfRecord[31].ToString().Trim(), out decimal tradingQty);//成交数量(CJSL)
decimal.TryParse(dbfRecord[36].ToString().Trim(), out decimal tradingAmount);//清算金额(QSJE)
decimal.TryParse(dbfRecord[45].ToString().Trim(), out decimal tradingPay);//实际收付(SJSF)
var underlyingCode = dbfRecord[23].ToString().Trim() + ".SH";// 证券代码1(ZQDM1)
var date = dbfRecord[11].ToString().Trim();//交易日期(JYRQ)
var tradeDate = ConvertDateTime(date);
var bsType = 0;
if (bstype == "B")
{
bsType = 1;
}
else if (bstype == "S")
{
bsType = 2;
}
swap_Flows.Add(new swap_flow()
{
FundAccount = dbfRecord[32].ToString().Trim(),//资金账号(ZJZH)
OccurTime = tradeDate,
UnderlyingCode = underlyingCode,
BsType = bsType,
TradingQty = tradingQty,
TradingAmount = tradingAmount,
TradingFee = tradingPay - tradingAmount,
DataState = 1
});
}
}
return swap_Flows;
}
/// <summary>
/// 6位字符串日期转换
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
private DateTime? ConvertDateTime(string date)
{
if (date.Length != 8)
{
return null;
}
DateTime.TryParse(date.Substring(0, 4) + "-" + date.Substring(4, 2) + "-" + date.Substring(6), out DateTime tradeDate);
return tradeDate;
}
}
}