从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
+25
View File
@@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
+162
View File
@@ -0,0 +1,162 @@
# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行
root = true
# c# 文件
[*.cs]
#### Core EditorConfig 选项 ####
# 缩进和间距
indent_size = 4
indent_style = space
tab_width = 4
# 新行首选项
end_of_line = crlf
insert_final_newline = false
#### .NET 编码约定 ####
# 组织 Using
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# this. 和 Me. 首选项
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# 语言关键字与 bcl 类型首选项
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# 括号首选项
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# 修饰符首选项
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# 表达式级首选项
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# 字段首选项
dotnet_style_readonly_field = true:suggestion
# 参数首选项
dotnet_code_quality_unused_parameters = all:suggestion
#### c# 编码约定 ####
# var 首选项
csharp_style_var_elsewhere = true:silent
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
# Expression-bodied 成员
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# 模式匹配首选项
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null 检查首选项
csharp_style_conditional_delegate_call = true:suggestion
# 修饰符首选项
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# 代码块首选项
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
# 表达式级首选项
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# "using" 指令首选项
csharp_using_directive_placement = outside_namespace:silent
#### C# 格式规则 ####
# 新行首选项
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# 缩进首选项
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# 空格键首选项
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# 包装首选项
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
# CA1819: Properties should not return arrays
dotnet_diagnostic.CA1819.severity = silent
+227
View File
@@ -0,0 +1,227 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
build/
bld/
[Bb]in/
[Oo]bj/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
## *.pubxml
# NuGet Packages
packages/*
*.nupkg
## TODO: If the tool you use requires repositories.config
## uncomment the next line
#!packages/repositories.config
# Enable "build/" folder in the NuGet Packages folder since
# NuGet packages use it for MSBuild targets.
# This line needs to be after the ignore of the build folder
# (and the packages folder if the line above has been uncommented)
!packages/build/
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
packages
/YieldChainExecution
.vs
Log
/YLErpWeb/App_Docs/GTJA/20181212
/YLErpWeb/App_Docs/GTJA/20181211
/YLErpWeb/App_Docs/TradeMarket
/YLErpWeb/App_Docs/GTJA/20181213
/YLErpWeb/App_Docs/付款申请单_申万上海有限公司.xlsx
/YLErpWeb/App_Docs/付款申请单_吕子乔.xlsx
/YLErpWeb/App_Docs/付款申请单1.xlsx
/YLErpWeb/App_Docs/付款申请单.doc
/YLErpWeb/App_Docs/20181217/付款申请单_吕子乔.xlsx
/YLErpWeb/App_Docs/20181217
/YLErpWeb/App_Docs/GTJA/20181217
/YLErpWeb/App_Docs/GTJA/20181218
/YLErpWeb/App_Docs/contractdoc/output
/YLErpWeb/438ae98b5f2e4833900c32bfff5f5fad.txt
/YLErpWeb/ClientFiles
/YLErpWeb/App_Plugin
/YLErpWeb/App_Docs
/ClientWeb/NLog.config
App_Upload
/Tools/DLL/LiveCharts.Wpf.dll
/Tools/DLL/LiveCharts.dll
/Tools/ExternalPythonEngineTest/__pycache__
/YLErpWeb/.vscode
PublishProfiles
.vscode
logfile
/YLWebAPI/QuartzSettings/logs
/YLErpWeb/App_Data/Config/otcformat.js
/YLErpWeb/appsettings.Development.json
/YLErpWeb/App_Data/Config/Config.json
/YLErpWeb/web.config
/YLWebAPI/web.config
/YLWebAPI/appsettings.Development.json
/YLManageAPI/appsettings.Development.json
/YLWebAPI/App_Docs
/.idea/.idea.YLerpbase/.idea
/NuGet.Config
+2
View File
@@ -0,0 +1,2 @@
for /f "tokens=*" %%a in ('dir obj /b /ad /s ^|sort') do rd "%%a" /s/q
for /f "tokens=*" %%a in ('dir bin /b /ad /s ^|sort') do rd "%%a" /s/q
+32
View File
@@ -0,0 +1,32 @@
using System.Text;
namespace YLWebAPI.ApiModule
{
static class ApiHelper
{
/// <summary>
///
/// </summary>
public static HttpResponseMessage GetResponse(int errcode, string errmsg)
{
var json = JsonHelper.Serialize(new ApiResponseModel
{
errcode = errcode,
errmsg = errmsg
});
return new HttpResponseMessage
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
}
/// <summary>
///
/// </summary>
public static HttpResponseMessage GetResponse(int errcode, Exception exception)
{
return GetResponse(errcode, "发生错误!" + exception?.Message);
}
}
}
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Newtonsoft.Json;
namespace YLWebAPI.ApiModule
{
/// <summary>
/// application/json模型绑定(支持空body,避免报400 error:A non-empty request body is required.)
/// </summary>
internal class JsonBodyBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!context.Metadata.ModelType.IsValueType)
{
return new BinderTypeModelBinder(typeof(EmptyBodyModelBinder));
}
return null;
}
class EmptyBodyModelBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.HttpContext.Request.ContentType?.Contains("application/json") != true)
{
return;
}
var stream = bindingContext.HttpContext.Request.Body;
using var reader = new StreamReader(stream);
var jsonbody = await reader.ReadToEndAsync();
if (jsonbody.Length > 0)
{
var obj = JsonConvert.DeserializeObject(jsonbody, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(obj);
}
else
{
bindingContext.Result = ModelBindingResult.Success(null);
}
}
}
}
}
@@ -0,0 +1,80 @@
using DocumentFormat.OpenXml.Spreadsheet;
using System.Security.Principal;
using YLErp;
namespace YLWebAPI.ApiModule
{
public class LoadUserMiddleware
{
readonly RequestDelegate next;
public LoadUserMiddleware(RequestDelegate next)
{
this.next = next;
}
/// <summary>
/// 请求拦截处理
/// </summary>
/// <param name="context">HTTP请求</param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext context)
{
if (context.User.Identity?.IsAuthenticated == true)
{
var clientId = GetValueFromClaims(context, "clientId");
_ = int.TryParse(GetValueFromClaims(context, "userId"), out int userId);
var userName = GetValueFromClaims(context, "userName");
var userGroup = GetValueFromClaims(context, "userGroup");
context.Items["ClientId"] = clientId;
var userinfo = new AuthUserInfo(userId, userName, userGroup)
{
IpAddress = GetClientIpAddress(context)
};
context.User = new GenericPrincipal(userinfo, null);
}
await next(context);
}
private static string GetValueFromClaims(HttpContext context, string claimKey)
{
var claim = context.User.FindFirst(claimKey);
if (claim != null)
{
return claim.Value;
}
return string.Empty;
}
private static string GetClientIpAddress(HttpContext context)
{
return context.Connection.RemoteIpAddress?.ToString() + context.Connection.RemotePort;
}
public class AuthUserInfo : OptUserInfo, IIdentity
{
/// <summary>
/// 用户名
/// </summary>
public string Name => UserName;
public string AuthenticationType => "JWT";
public bool IsAuthenticated => true;
public AuthUserInfo(int userId, string userName, string userGroup)
: base(userId, userName, OptUserFrom.WebAPI, userGroup)
{
}
public string CreateJwtToken()
{
var identity = UserManager.CreateIdentity(UserId.ToString(), UserName, AuthHelper.JwtUserToken);
var jwtToken = AuthHelper.CreateJwtToken(identity);
return jwtToken;
}
}
}
}
+110
View File
@@ -0,0 +1,110 @@
using System.Collections;
using System.Data;
using YLErp;
namespace YLWebAPI.ApiModule
{
[ApiController]
public abstract class BaseApiController : ControllerBase
{
OptUserInfo _userInfo;
protected OptUserInfo CurUser
{
get
{
if (_userInfo != null)
{
return _userInfo;
}
if (User.Identity is OptUserInfo userInfo)
{
return _userInfo = userInfo;
}
return OptUserInfo.SystemUser;
}
}
#region----JSON消息处理----
/// <summary>
/// 处理成功后返回的json消息
/// </summary>
protected ApiResponseModel JsonSuccessInfo(string message)
{
return new ApiResponseModel { errcode = 0, errmsg = message };
}
/// <summary>
/// 处理成功后返回的json数据
/// </summary>
protected ApiResponseModel JsonSuccess()
{
return new ApiResponseModel { errcode = 0, errmsg = string.Empty };
}
/// <summary>
/// 处理成功后返回的json数据
/// </summary>
protected ApiResponseModel JsonSuccess(object data, string message = null)
{
return new ApiResponseModel { errcode = 0, errmsg = message, data = data };
}
/// <summary>
/// 处理错误时返回的json消息
/// </summary>
protected ApiResponseModel JsonError(string errmsg)
{
return new ApiResponseModel { errcode = 1, errmsg = errmsg };
}
/// <summary>
/// 处理错误时返回的json消息
/// </summary>
protected ApiResponseModel JsonError(int errcode, string errmsg)
{
return new ApiResponseModel { errcode = errcode, errmsg = errmsg };
}
/// <summary>
/// 处理错误时返回的json消息
/// </summary>
protected ApiResponseModel JsonError(Exception ex)
{
return new ApiResponseModel { errcode = 1, errmsg = "发生错误!" + ex.Message };
}
/// <summary>
/// 返回的分页json数据
/// </summary>
protected ApiResponseModel JsonPagedList(int pageIndex, int totalCount, IEnumerable rows)
{
return new ApiResponseModel
{
data = new { page = pageIndex, total = totalCount, rows }
};
}
/// <summary>
/// 返回的分页json数据
/// </summary>
protected ApiResponseModel JsonPagedList<T>(IPagedList<T> pagedList)
{
return new ApiResponseModel
{
data = new
{
page = pagedList.PageIndex,
total = pagedList.TotalCount,
rows = pagedList.Items,
totalPages = pagedList.TotalPages()
}
};
}
#endregion
}
}
@@ -0,0 +1,278 @@
using YLErp.Modules.ClientModule;
using YLErp.Modules.SubAppModule.ClientApp;
using YLErp.Modules.TradeDalModule;
namespace YLWebAPI.ApiModule.ClientApi
{
/// <summary>
/// 专用于客户端的API列表(路径样式:api/v1/client/*)
/// </summary>
[ClientAuth]
public class ClientApiController : BaseApiController
{
[HttpPost, Route("api/v1/client/test")]
public ApiResponseModel Test([FromBody] ClientChangePasswordRequest reqModel)
{
throw new System.Exception("测试报错");
}
[HttpPost, Route("api/v1/client/changeClientPassword")]
public ApiResponseModel ChangeClientPassword([FromBody] ClientChangePasswordRequest reqModel)
{
new ClientUserPasswordService(CurUser).ChangePassword(reqModel);
return JsonSuccess("成功", "成功");
}
/// <summary>
/// 结算报告:获取客户财务状况
/// </summary>
[HttpPost, Route("api/v1/client/clientLatestBalance")]
public IActionResult ClientLatestBalance([FromBody] ClientLatestBalanceRequestModel reqModel)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息"));
}
if (ids.Length > 1)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求"));
}
reqModel.ClientId = ids[0];
}
ClientSettleBalance data = null;
if (reqModel.ClientId > 0)
{
data = ClientAssetDataService.GetClientLatestBalance(
reqModel.ValueDateFrom,
reqModel.ValueDateTo,
reqModel.ClientId);
}
return Ok(data);
}
/// <summary>
/// 结算报告:获取客户持仓信息
/// </summary>
[HttpPost, Route("api/v1/client/clientTradePosition")]
public IActionResult ClientTradePosition([FromBody] ClientTradePositionRequestModel reqModel)
{
SearchListResult<eod_position> data = null;
if (reqModel.ClientId > 0)
{
data = new ClientPositionQueryService(CurUser).GetClientPositionAPI(
reqModel.ValueDate,
reqModel.ClientId,
reqModel.Page,
reqModel.PageRowCount);
}
return Ok(data);
}
/// <summary>
/// 结算报告:历史交易
/// </summary>
[HttpPost, Route("api/v1/client/clientTradeHistory")]
public IActionResult ClientTradeHistory([FromBody] ClientLatestBalancePagedRequestModel reqModel)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息"));
}
if (ids.Length > 1)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求"));
}
reqModel.ClientId = ids[0];
}
SearchListResult<trade_contract_group_simple> data = null;
if (reqModel.ClientId > 0)
{
data = ClientAssetDataService.GetClientTradeHistory(
reqModel.ValueDateFrom,
reqModel.ValueDateTo,
reqModel.ClientId,
reqModel.Page ?? 0,
reqModel.PageRowCount ?? 0);
}
return Ok(data);
}
/// <summary>
/// 结算报告:查看交易详情
/// </summary>
[HttpPost, Route("api/v1/client/tradeDetails")]
public IActionResult TradeDetails([FromBody] ClientTradeDetailRequestModel reqModel)
{
TradeWithSettlement data = null;
if (!string.IsNullOrWhiteSpace(reqModel.EncryptedId))
{
var tradeId = DataProtectHelper.DecryptInt(reqModel.EncryptedId);
var trade = YLErp.Modules.DbContextFactory.GetYLDbContext().trade.Find(tradeId);
//客户角度看交易,买卖方向反过来
if (trade != null)
{
data = new TradeDalService(CurUser).GetTradeWithSettlementTradeCash(trade);
if (trade.BuySell == "买入")
{
data.Trade.BuySell = "卖出";
}
else if (trade.BuySell == "卖出")
{
data.Trade.BuySell = "买入";
}
if (data.MaturityTradeCash != null)
{
data.MaturityTradeCash.SingleWinPrice = -data.MaturityTradeCash.SingleWinPrice;
data.MaturityTradeCash.WinLoss = MinusNumberString(data.MaturityTradeCash.WinLoss);
}
if (data.UnwindTradeCashList != null)
{
data.UnwindTradeCashList.ForEach(tradeCash =>
{
tradeCash.WinLoss = MinusNumberString(tradeCash.WinLoss);
});
}
}
}
return Ok(data);
}
private static string MinusNumberString(string numberStr)
{
double number;
if (double.TryParse(numberStr, out number))
{
return (-number).ToString();
}
else
{
return "";
}
}
/// <summary>
/// 结算报告:资金记录
/// </summary>
[HttpPost, Route("api/v1/client/clientCashRecords")]
public IActionResult ClientCashRecords([FromBody] ClientLatestBalancePagedRequestModel reqModel)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息"));
}
if (ids.Length > 1)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求"));
}
reqModel.ClientId = ids[0];
}
SearchListResult<ClientCashInCashOutExtend> data = null;
if (reqModel.ClientId > 0)
{
data = ClientAssetDataService.GetClientCashRecords(
reqModel.ValueDateFrom,
reqModel.ValueDateTo,
reqModel.ClientId,
reqModel.Page ?? 0,
reqModel.PageRowCount ?? 0);
}
return Ok(data);
}
/// <summary>
/// 结算报告:客户是否有过质押记录
/// </summary>
[HttpPost, Route("api/v1/client/clientHasCollateral")]
public ApiResponseModel ClientHasCollateral([FromBody] ClientLatestBalanceRequestModel reqModel)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息");
}
if (ids.Length > 1)
{
return JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求");
}
reqModel.ClientId = ids[0];
}
var hasCollateral = false;
if (reqModel.ClientId > 0)
{
using (var db = YLErp.Modules.DbContextFactory.GetYLDbContext())
{
hasCollateral = db.clientcashincashout_product.Count(x => x.ClientId == reqModel.ClientId) > 0;
}
}
//必须包一层
return JsonSuccess(hasCollateral);
}
/// <summary>
/// 结算报告:质押记录
/// </summary>
[HttpPost, Route("api/v1/client/clientCollateral")]
public IActionResult ClientCollateral([FromBody] ClientLatestBalanceRequestModel reqModel)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息"));
}
if (ids.Length > 1)
{
return Ok(JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求"));
}
reqModel.ClientId = ids[0];
}
SearchListResult<clientcashincashout_productLinq> data = null;
if (reqModel.ClientId > 0)
{
var req = new clientcashincashout_productReq()
{
ValueDate = reqModel.ValueDateTo,
ClientId = reqModel.ClientId
};
data = new clientcashincashout_productBLL().SearchListForMarketReport(req);
}
return Ok(data);
}
/// <summary>
/// 返回客户端声明
/// </summary>
[HttpGet, Route("api/v1/client/clientDesc")]
public ApiResponseModel ClientDesc()
{
using (var db = YLErp.Modules.DbContextFactory.GetYLDbContext())
{
var description = db.valuedate.FirstOrDefault(v => v.State == valuedate.使).ClientDesc;
return JsonSuccess(description);//必须包一层
}
}
}
}
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Authorization;
namespace YLWebAPI.ApiModule.ClientApi
{
/// <summary>
/// 客户端认证属性
/// </summary>
public class ClientAuthAttribute : AuthorizeAttribute
{
public const string SecretKey = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrkzI1NiJ9eyJjbGF";
public ClientAuthAttribute() : base("client-side")
{
}
}
}
@@ -0,0 +1,92 @@
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using System.Text;
namespace YLWebAPI.ApiModule.ClientApi
{
/// <summary>
/// 客户端认证token
/// </summary>
public class ClientTokenController : BaseApiController
{
[Route("api/clientToken")]
[HttpPost]
public IActionResult clientToken()
{
if (!Request.Headers.TryGetValue("Authorization", out var authHeaders))
{
return Ok(JsonError("[HTTP Header:Authorization]参数未设置"));
}
var authHeader = authHeaders.FirstOrDefault() ?? string.Empty;
if (!authHeader.StartsWith("Basic "))
{
return Ok(JsonError("[HTTP Header:Authorization]参数错误,关键字:Basic"));
}
int index;
string joinStr;
try
{
var bytes = Convert.FromBase64String(authHeader.Substring(6));
joinStr = Encoding.UTF8.GetString(bytes);
index = joinStr.IndexOf(':');
if (index <= 0)
{
return Ok(JsonError("[HTTP Header:Authorization]参数错误,关键字:Value"));
}
}
catch (Exception ex)
{
return Ok(JsonError(ex.GetBaseException().Message));
}
var userName = joinStr.Substring(0, index);
var password = joinStr.Substring(index + 1);
using (var clientDB = YLErp.Modules.DbContextFactory.GetClientDbContext(CurUser))
{
var user = clientDB.ClientUser.Where(n => n.LoginName == userName).FirstOrDefault();
if (user == null)
{
return Ok(JsonError("用户或密码错误"));
}
if (!DataHelper.EncryptPassword(user.LoginName.ToLowerInvariant(), password).Equals(user.Password, StringComparison.OrdinalIgnoreCase))
{
return Ok(JsonError("用户或密码错误"));
}
var client = clientDB.client.Where(c => c.id == user.ClientId).Select(n => new { n.Guid, n.Name }).FirstOrDefault();
if (client == null)
{
return Ok(JsonError("客户信息不存在"));
}
var token = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSerializer(new JsonNetSerializer())
.WithSecret(ClientAuthAttribute.SecretKey)
.AddClaim("exp", DateTimeOffset.UtcNow.AddSeconds(36100).ToUnixTimeSeconds())
.AddClaim("userId", user.id)
.AddClaim("userName", user.LoginName)
.Encode();
return Ok(new ClientApiTokenResponseModel
{
tokenType = "Bearer",
accessToken = token,
expiresIn = 36000,
userId = user.id,
userName = client.Name,
userGroup = string.Empty,
clientId = user.ClientId,
guid = client.Guid
});
}
}
}
}
@@ -0,0 +1,27 @@
using YLErp.BLL.Eod;
namespace YLWebAPI.ApiModule.ClientApi
{
public class SystemDataController : BaseApiController
{
/// <summary>
/// 获取最后一个结算日
/// </summary>
[HttpGet, Route("api/v1/client/lastSettlementDate")]
public ApiResponseModel clientLastSettlementDate()
{
var data = EodOperationBase.GetLastSettlementDate(DateTime.Now);
return JsonSuccess(data);
}
/// <summary>
/// 获取系统当前日期
/// </summary>
[HttpGet, Route("api/v1/client/systemDate")]
public ApiResponseModel systemDate()
{
var data = valuedateBLL.ValueDate;
return JsonSuccess(data);
}
}
}
@@ -0,0 +1,32 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace YLErp.Converter2
{
public class EnumConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null || string.IsNullOrEmpty(reader.Value?.ToString()))
{
return existingValue;
}
else
{
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
}
else
{
writer.WriteValue(value);
}
}
}
}
@@ -0,0 +1,39 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class IsoDateTimeConverter : JsonConverter<DateTime>
{
public IsoDateTimeConverter() { }
public IsoDateTimeConverter(string format)
{
DateTimeFormat = format;
}
public string DateTimeFormat { get; set; } = "yyyy/MM/dd";
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var value = reader.Value?.ToString();
if (string.IsNullOrEmpty(value))
{
return existingValue;
}
else
{
if (DateTime.TryParse(value, out var date))
{
return date;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
{
writer.WriteValueAsync(value.ToString(DateTimeFormat)).Wait();
}
}
}
@@ -0,0 +1,48 @@
using Newtonsoft.Json;
using System;
using System.Globalization;
namespace YLErp.Converter2
{
public class IsoDateTimeConverterNullable : JsonConverter<DateTime?>
{
public IsoDateTimeConverterNullable() { }
public IsoDateTimeConverterNullable(string format)
{
DateTimeFormat = format;
}
public string DateTimeFormat { get; set; } = "yyyy/MM/dd";
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (string.IsNullOrWhiteSpace(reader.Value?.ToString()) && (Nullable.GetUnderlyingType(objectType) != null))
{
return null;
}
DateTime date;
try
{
date = DateTime.ParseExact(reader.Value?.ToString(), DateTimeFormat, CultureInfo.InvariantCulture);
}
catch (Exception)
{
date = DateTime.Parse(reader.Value?.ToString());
}
return date;
}
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValue(value.Value.ToString(DateTimeFormat));
}
}
}
}
@@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedBooleanConverter : JsonConverter<bool>
{
public override bool ReadJson(JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (Boolean.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, bool value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,42 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedBooleanConverter_Nullable : JsonConverter<bool?>
{
public override bool? ReadJson(JsonReader reader, Type objectType, bool? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return false;
}
if (Boolean.TryParse(reader.ReadAsString(), out bool number))
return number;
}
return reader.ReadAsBoolean();
}
public override void WriteJson(JsonWriter writer, bool? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedByteConverter : JsonConverter<byte>
{
public override byte ReadJson(JsonReader reader, Type objectType, byte existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (byte.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, byte value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,49 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedByteConverter_Nullable : JsonConverter<byte?>
{
public override byte? ReadJson(JsonReader reader, Type objectType, byte? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return default(byte);
}
if (Byte.TryParse(reader.ReadAsString(), out byte number))
return number;
}
var result = reader.ReadAsBytes();
if (result != null)
{
return result.First();
}
else
{
return default(byte);
}
}
public override void WriteJson(JsonWriter writer, byte? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedDecimalConverter : JsonConverter<decimal>
{
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (decimal.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedDecimalConverter_Nullable : JsonConverter<decimal?>
{
private const decimal defaultValue = default(decimal);
public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (decimal.TryParse(reader.ReadAsString(), out decimal number))
return number;
}
return reader.ReadAsDecimal();
}
public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedDoubleConverter : JsonConverter<double>
{
public override double ReadJson(JsonReader reader, Type objectType, double existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (double.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, double value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedDoubleConverter_Nullable : JsonConverter<double?>
{
private const double defaultValue = default(double);
public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (double.TryParse(reader.ReadAsString(), out double number))
return number;
}
return reader.ReadAsDouble();
}
public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedFloatConverter : JsonConverter<float>
{
public override float ReadJson(JsonReader reader, Type objectType, float existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (float.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, float value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedFloatConverter_Nullable : JsonConverter<float?>
{
private const float defaultValue = default(float);
public override float? ReadJson(JsonReader reader, Type objectType, float? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (float.TryParse(reader.ReadAsString(), out float number))
return number;
}
return (float?)reader.Value;
}
public override void WriteJson(JsonWriter writer, float? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedInt16Converter : JsonConverter<short>
{
public override short ReadJson(JsonReader reader, Type objectType, short existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (short.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, short value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedInt16Converter_Nullable : JsonConverter<Int16?>
{
private const Int16 defaultValue = default(Int16);
public override Int16? ReadJson(JsonReader reader, Type objectType, Int16? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (Int16.TryParse(reader.ReadAsString(), out Int16 number))
return number;
}
return (Int16?)reader.Value;
}
public override void WriteJson(JsonWriter writer, Int16? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedInt32Converter : JsonConverter<int>
{
public override int ReadJson(JsonReader reader, Type objectType, int existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (int.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, int value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedInt32Converter_Nullable : JsonConverter<Int32?>
{
private const Int32 defaultValue = default(Int32);
public override Int32? ReadJson(JsonReader reader, Type objectType, Int32? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.Value?.ToString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (Int32.TryParse(reader.ReadAsString(), out Int32 number))
return number;
}
return (Int32?)reader.Value;
}
public override void WriteJson(JsonWriter writer, Int32? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace YLErp.Converter2
{
public class QuotedInt64Converter : JsonConverter<long>
{
public override long ReadJson(JsonReader reader, Type objectType, long existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return existingValue;
}
else
{
var str = reader.Value?.ToString();
if (string.IsNullOrEmpty(str))
{
return existingValue;
}
if (long.TryParse(str, out var number))
{
return number;
}
else
{
return existingValue;
}
}
}
public override void WriteJson(JsonWriter writer, long value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedInt64Converter_Nullable : JsonConverter<Int64?>
{
private const Int64 defaultValue = default(Int64);
public override Int64? ReadJson(JsonReader reader, Type objectType, Int64? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (Int64.TryParse(reader.ReadAsString(), out Int64 number))
return number;
}
return (Int64?)reader.Value;
}
public override void WriteJson(JsonWriter writer, Int64? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Buffers.Text;
namespace YLErp.Converter
{
public class QuotedLongConverter_Nullable : JsonConverter<long?>
{
private const long defaultValue = default(long);
public override long? ReadJson(JsonReader reader, Type objectType, long? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
if (string.IsNullOrWhiteSpace(reader.ReadAsString()))
{
if (Nullable.GetUnderlyingType(objectType) != null)
{
return null;
}
return defaultValue;
}
if (long.TryParse(reader.ReadAsString(), out long number))
return number;
}
return (long?)reader.Value;
}
public override void WriteJson(JsonWriter writer, long? value, JsonSerializer serializer)
{
if (!value.HasValue)
{
writer.WriteNull();
}
else
{
writer.WriteValueAsync(value);
}
}
}
}
@@ -0,0 +1,22 @@
namespace YLErp.Converter2
{
public static class SerializerOptionsConfigration
{
public static void InitConverter(this MvcNewtonsoftJsonOptions options)
{
options.SerializerSettings.Converters.Add(new IsoDateTimeConverter());
options.SerializerSettings.Converters.Add(new QuotedBooleanConverter());
options.SerializerSettings.Converters.Add(new QuotedDecimalConverter());
options.SerializerSettings.Converters.Add(new QuotedDoubleConverter());
options.SerializerSettings.Converters.Add(new QuotedFloatConverter());
options.SerializerSettings.Converters.Add(new QuotedInt16Converter());
options.SerializerSettings.Converters.Add(new QuotedInt32Converter());
options.SerializerSettings.Converters.Add(new QuotedInt64Converter());
options.SerializerSettings.Converters.Add(new QuotedByteConverter());
options.SerializerSettings.Converters.Add(new EnumConverter());
//options.SerializerSettings.Converters.Add(new QuoteJsonStringEnumConverter()); // 处理字符串转枚举
//options.SerializerSettings.Converters.Add(new NullableConverterFactory()); // 处理Nullable
//options.SerializerSettings.Converters.Add(new StringConverter()); // 数字转字符串
}
}
}
@@ -0,0 +1,50 @@
using Newtonsoft.Json;
namespace YLErp.Converter
{
public class StringConverter : JsonConverter<string>
{
//public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
//{
// if (reader.TokenType == JsonTokenType.String)
// {
// return reader.GetString();
// }
// else
// {
// //非字符类型,返回原生内容
// return GetRawPropertyValue(reader);
// }
//}
//public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
//{
// writer.WriteStringValue(value);
//}
///// <summary>
///// 非字符类型,返回原生内容
///// </summary>
///// <param name="jsonReader"></param>
///// <returns></returns>
//private static string GetRawPropertyValue(JsonReader jsonReader)
//{
// ReadOnlySpan<byte> utf8Bytes = jsonReader.HasValueSequence ?
// jsonReader.ValueSequence.ToArray() :
// jsonReader.ValueSpan;
// return Encoding.UTF8.GetString(utf8Bytes);
//}
public override void WriteJson(JsonWriter writer, string? value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
public override string? ReadJson(JsonReader reader, Type objectType, string? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return reader.ReadAsString();
}
}
}
@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
using System.Text;
namespace SharedWebApi.Filter
{
public class ActionLogFilter : IActionFilter
{
private string RequestBody { get; set; }
private Stopwatch Stopwatch { get; set; }
private string ActionArguments { get; set; }
private static readonly IYcLogger logger = LogFactory.GetLogger("api");
public void OnActionExecuted(ActionExecutedContext context)
{
Stopwatch.Stop();
var url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
var method = context.HttpContext.Request.Method;
var qs = ActionArguments;
dynamic result = context.Result.GetType().Name == "EmptyResult" ? new { Value = "无返回结果" } : context.Result;
var res = "在返回结果前发生了异常";
try
{
if (result != null)
{
res = Newtonsoft.Json.JsonConvert.SerializeObject(result.Value);
}
}
catch (Exception)
{
res = "日志未获取到结果,返回的数据无法序列化";
}
logger.Debug($"地址:{url} \n " +
$"方式:{method} \n " +
$"请求体:{RequestBody} \n " +
$"参数:{qs}\n " +
$"结果:{res}\n " +
$"耗时:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒(指控制器内对应方法执行完毕的时间)");
}
public async void OnActionExecuting(ActionExecutingContext context)
{
var contentLen = context.HttpContext.Request.ContentLength ?? 0;
if (contentLen > 0)
{
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(context.HttpContext.Request.Body, encoding: Encoding.UTF8);
RequestBody = await reader.ReadToEndAsync();
}
ActionArguments = Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments);
Stopwatch = new Stopwatch();
Stopwatch.Start();
}
}
}
+1
View File
@@ -0,0 +1 @@
global using YieldChain.Models;
@@ -0,0 +1,70 @@
using Qdp.Pricing.Base.Implementations;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 工具类API
/// </summary>
[ManagerAuth]
public class CalendarController : BaseApiController
{
/// <summary>
/// 工作日天数计算
/// 单元测试:CalendarTest.TestGetWorkingDayCount
/// </summary>
[HttpPost]
[Route("api/v1/calendar/get_workingdaycount")]
public ApiResponseModel GetWorkingDayCount([FromBody] GetWorkingDayCountRequest req)
{
if (req is null)
{
return JsonError("参数不能为空");
}
if (req.StartDate.Year < 2000)
{
return JsonError("StartDate需为2000年之后的日期");
}
var daycount = 0;
var calendar = CalendarImpl.Get(req.CalendarName.TrimToNull() ?? "chn");
DateTime? startDate = null, endDate = null;
for (var date = req.StartDate; date <= req.EndDate; date = date.AddDays(1))
{
if (!calendar.IsHoliday(date))
{
startDate = date;
break;
}
}
if (startDate.HasValue)
{
daycount = 1;
endDate = startDate;
for (var date = req.EndDate; date > startDate.Value; date = date.AddDays(-1))
{
if (!calendar.IsHoliday(date))
{
endDate = date;
break;
}
}
for (var date = startDate.Value; date < endDate.Value; date = date.AddDays(1))
{
if (!calendar.IsHoliday(date))
{
daycount++;
}
}
}
return JsonSuccess(new { DayCount = daycount, StartDate = startDate, EndDate = endDate });
}
}
}
@@ -0,0 +1,112 @@
using YLErp.Modules.ClientModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 用于管理端的客户资金API
/// </summary>
//[ManagerAuth]
public class ClientCashController : BaseApiController
{
/// <summary>
/// 获取客户最新资金概况
/// </summary>
[HttpPost]
[Route("api/v1/ClientBalance")]
public ApiResponseModel GetClientLatestBalance([FromBody] ClientLatestBalanceRequestModel req)
{
if (req.ClientId == 0)
{
if (!string.IsNullOrWhiteSpace(req.ClientNumber))
{
var ids = new ClientDataService(CurUser).GetClientIdByNumber(req.ClientNumber);
if (ids.Length == 0)
{
return JsonError($"根据客户编号'{req.ClientNumber}'没有找到客户信息");
}
if (ids.Length > 1)
{
return JsonError($"根据客户编号'{req.ClientNumber}'找到多个客户信息,不支持此种请求");
}
req.ClientId = ids[0];
}
else if (!string.IsNullOrWhiteSpace(req.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(req.ClientName);
if (ids.Length == 0)
{
return JsonError($"根据客户名称'{req.ClientName}'没有找到客户信息");
}
if (ids.Length > 1)
{
return JsonError($"根据客户名称'{req.ClientName}'找到多个客户信息,不支持此种请求");
}
req.ClientId = ids[0];
}
else
{
return JsonError($"客户ID/客户编号/客户名称 参数必须赋值");
}
}
else
{
var exsist = new ClientDataService(CurUser).ExsistById(req.ClientId);
if (!exsist)
{
return JsonError($"根据客户Id'{req.ClientId}'没有找到客户信息");
}
}
var ret = ClientAssetDataService.GetClientLatestBalance(
ValueDateFrom: req.ValueDateFrom, ValueDateTo: req.ValueDateTo,
clientId: req.ClientId, IsClientBalanceGap: false, IsGetOuterMarginGap: req.IsGetOuterMarginGap);
return JsonSuccess(ret);
}
/// <summary>
/// 客户出入金
/// </summary>
[HttpPost]
[Route("api/v1/ClientCashInCashOut")]
public ApiResponseModel ClientCashInCashOut([FromBody] SaveEntryexitApiReq req)
{
var res = new ClientCashInCashOutDataService(CurUser).SaveEntryexitApi(req);
var data = new { res.id, res.Number };
return JsonSuccess(data);
}
/// <summary>
/// 客户出入金记录确认
/// </summary>
[HttpPost]
[Route("api/v1/ClientCashInCashOutConfirm")]
public ApiResponseModel ClientCashInCashOutConfirm(int id)
{
new ClientCashInCashOutDataService(CurUser).ExcuteEntryExit(id);
return JsonSuccess("执行成功");
}
/// <summary>
/// 客户出入金记录拒绝
/// </summary>
[HttpPost]
[Route("api/v1/ClientCashInCashOutReject")]
public ApiResponseModel ClientCashInCashOutReject(int id)
{
new ClientCashInCashOutDataService(CurUser).ClientCashInCashOutReject(id);
return JsonSuccess("拒绝成功");
}
/// <summary>
/// 结算信息偏差值写入
/// </summary>
[HttpPost]
[Route("api/v1/ClientBalanceGap")]
public ApiResponseModel ClientBalanceGap(ClientBalanceGapReq req)
{
new ClientCashInCashOutDataService(CurUser).ClientBalanceGapApi(req);
return JsonSuccess("执行成功");
}
}
}
@@ -0,0 +1,374 @@
using SharedWebApi.Filter;
using System.Net;
using System.Net.Http.Headers;
using YLErp;
using YLErp.BLL.EodSettlement;
using YLErp.Modules.ClientModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 用于管理端的客户数据API
/// </summary>
[ManagerAuth]
public class ClientDataController : BaseApiController
{
/// <summary>
/// 获取客户编辑配置
/// </summary>
[HttpPost]
[Route("api/v1/getClientConfig")]
public ApiResponseModel GetClientConfig()
{
var config = YLErp.Modules.SystemModule.ClientEditConfigService.GetConfig(true);
config.openList = null;
config.clientList = null;
return JsonSuccess(config);
}
/// <summary>
/// 获取客户信息
/// </summary>
[HttpPost]
[Route("api/v1/getClientInfo")]
public ApiResponseModel GetClientInfo([FromBody] ClientInfoQueryModel queryModel)
{
if (string.IsNullOrWhiteSpace(queryModel?.ClientNumber) && string.IsNullOrWhiteSpace(queryModel.ClientName))
{
return JsonError("缺少必需的请求参数");
}
using var db = DbContextFactory.GetClientDbContext(CurUser);
var clientQuery = db.client.AsNoTracking();
var client = !string.IsNullOrWhiteSpace(queryModel.ClientNumber)
? clientQuery.FirstOrDefault(n => n.Number == queryModel.ClientNumber)
: clientQuery.FirstOrDefault(n => n.Name == queryModel.ClientName);
if (client == null)
{
return JsonError("没有找到客户信息");
}
client.MetaDic = new ClientMetaService(CurUser).GetMetaDic(client.id);
client.CreditRatingStr = new ClientRatingService(CurUser).GetCurrentCreditRating(valuedateBLL.ValueDate, client.id)?.Item2;
return JsonSuccess(client);
}
/// <summary>
/// 获取客户信息
/// </summary>
[HttpPost]
[Route("api/v1/getClientInfo2")]
public ApiResponseModel GetClientInfo2([FromBody] ClientInfoQueryModel queryModel)
{
if (queryModel is null)
{
return JsonError("缺少必需的请求参数");
}
var result = new ClientInfoQueryService(CurUser).GetClientInfo(queryModel);
if (result == null)
{
return JsonError("没有找到客户信息");
}
return JsonSuccess(result);
}
/// <summary>
/// 获取客户信息
/// </summary>
[HttpPost]
[Route("api/v1/client/view")]
public ApiResponseModel ClientView([FromBody] ClientInfoQueryModel queryModel)
{
if (queryModel is null)
{
return JsonError("缺少必需的请求参数");
}
var result = new ClientInfoQueryService(CurUser).GetClientInfo(queryModel);
if (result == null)
{
return JsonError("没有找到客户信息");
}
var ClientInfo = ClientInfoQueryService.ConvertToViewData(result.ClientInfo);
return JsonSuccess(new { ClientInfo, result.BankCards, result.Duties });
}
/// <summary>
/// 通过API创建客户
/// </summary>
[HttpPost]
[Route("api/v1/CreateClient")]
public ApiResponseModel CreateClient(CreateClientReq req)
{
new ClientSaveService(CurUser).CreateClient(req);
return JsonSuccess("更新成功");
}
/// <summary>
/// 通过API编辑(新增或者修改)客户
/// </summary>
[HttpPost]
[Route("api/v1/saveClient")]
public ApiResponseModel SaveClient(Client req)
{
var client = new ClientSaveService(CurUser).SaveClientAPI(req);
return JsonSuccess(new
{
ClientId = client.id,
ClientName = client.Name,
ClientNumber = client.Number
});
}
/// <summary>
/// 保存客户人员信息V2
/// </summary>
[HttpPost, Route("api/v2/saveClient")]
public ApiResponseModel SaveClientV2([FromBody] Dictionary<string, object> model)
{
var result = new ClientSaveApiV2Service(CurUser).Save(model);
return JsonSuccess(result);
}
/// <summary>
/// 客户信息列表
/// </summary>
[HttpPost, Route("api/v1/clientInfoList")]
public ApiResponseModel ClientInfoList()
{
// 查询客户信息
using var db = DbContextFactory.GetClientDbContext(CurUser);
var clients = db.client.Where(n => n.ProcessStatus == "已开户")
.Select(n => new
{
ClientId = n.id,
ClientName = n.Name,
ClientNumber = n.Number,
n.ClientType,
n.LicenseCode, //证照编号
n.LicenseType
}).ToArray();
// 查询授信信息
using var yldb = DbContextFactory.GetYLDbContext();
var credits = yldb.credit.Where(g => clients.Select(b => b.ClientId).ToArray().Contains(g.ClientId ?? 0) && valuedateBLL.ValueDate >= g.CreditStartDate && valuedateBLL.ValueDate <= g.CreditDeadLine)
.ToArray().GroupBy(g => g.ClientId).Select(g => g.FirstOrDefault()).ToArray();
// yldb.ClientBalanceDaily.Where()
var result = (from client in clients
join credit in credits on client.ClientId equals credit.ClientId into creditTmp
from creditT in creditTmp.DefaultIfEmpty()
select new
{
client.ClientId,
client.ClientName,
client.ClientNumber,
client.ClientType,
client.LicenseCode, //证照编号
client.LicenseType,
creditT?.CreditStartDate,
creditT?.Credit,
creditT?.CreditDeadLine,
MetaDic = new Dictionary<string, string>()
}).ToList();
if (PS.Config.Company == CompanyEnum.)
{
var ids = clients.Select(b => b.ClientId).ToArray();
var dicMetas = db.ClientMeta.Where(g => ids.Contains(g.ClientId) && g.MetaKey == "CustomerScore")
.Select(n => new { n.ClientId, n.MetaValue })
.AsEnumerable().ToDictionary(l => l.ClientId, l => l.MetaValue);
foreach (var item in result)
{
item.MetaDic["资信评分"] = dicMetas.TryGetValue(item.ClientId, out var CustomerScore) ? CustomerScore : "";
}
}
return JsonSuccess(result);
}
/// <summary>
/// 客户持仓查询
/// </summary>
//[TypeFilter(typeof(ActionLogFilter))]
[HttpPost, Route("api/v1/clientPositionList")]
public ApiResponseModel ClientPositionList([FromBody] ClientPositionQueryModel reqModel)
{
reqModel ??= new ClientPositionQueryModel();
var pagedList = new ClientPositionService(CurUser).GetAllPositionsV1(reqModel);
return JsonPagedList(pagedList);
}
/// <summary>
/// 客户持仓查询
/// </summary>
//[TypeFilter(typeof(ActionLogFilter))]
[HttpPost, Route("api/v2/clientPositionList")]
public ApiResponseModel ClientPositionListV2([FromBody] ClientPositionQueryModel reqModel)
{
reqModel ??= new ClientPositionQueryModel();
var pagedList = new ClientPositionService(CurUser).GetAllPositionsV2(reqModel);
return JsonPagedList(pagedList);
}
/// <summary>
/// 客户持仓查询
/// </summary>
//[TypeFilter(typeof(ActionLogFilter))]
[HttpPost, Route("api/v3/clientPositionList")]
public ApiResponseModel ClientPositionListV3([FromBody] ClientPositionQueryModel reqModel)
{
reqModel ??= new ClientPositionQueryModel();
var pagedList = new ClientPositionService(CurUser).GetAllPositionsV3(reqModel);
return JsonPagedList(pagedList);
}
/// <summary>
/// 结算报告:获取客户财务状况
/// </summary>
[HttpPost, Route("api/v1/clientLatestBalanceList")]
public ApiResponseModel ClientLatestBalanceList([FromBody] ClientLatestBalanceRequestModel reqModel)
{
if (reqModel is null)
{
reqModel = new ClientLatestBalanceRequestModel();
}
else if (reqModel.ClientId == 0)
{
if (!string.IsNullOrWhiteSpace(reqModel.ClientNumber))
{
var ids = new ClientDataService(CurUser).GetClientIdByNumber(reqModel.ClientNumber);
if (ids.Length == 0)
{
return JsonError($"根据客户编号'{reqModel.ClientNumber}'没有找到客户信息");
}
if (ids.Length > 1)
{
return JsonError($"根据客户编号'{reqModel.ClientNumber}'找到多个客户信息,不支持此种请求");
}
reqModel.ClientId = ids[0];
}
else if (!string.IsNullOrWhiteSpace(reqModel.ClientName))
{
var ids = new ClientDataService(CurUser).GetClientIdByName(reqModel.ClientName);
if (ids.Length == 0)
{
return JsonError($"根据客户名称'{reqModel.ClientName}'没有找到客户信息");
}
if (ids.Length > 1)
{
return JsonError($"根据客户名称'{reqModel.ClientName}'找到多个客户信息,不支持此种请求");
}
reqModel.ClientId = ids[0];
}
}
if (reqModel.ClientId > 0)
{
var datas = ClientBalanceUtility.GetClientBanlances(
clientIdList: new List<int> { reqModel.ClientId },
startDate: reqModel.ValueDateFrom ?? DateTime.MinValue,
endDate: reqModel.ValueDateTo ?? YLErp.BLL.valuedateBLL.ValueDate,
IsGetOuterMarginGap: reqModel.IsGetOuterMarginGap);
return JsonSuccess(datas);
}
else
{
var datas = ClientBalanceUtility.GetAllClientBanlances(
startDate: reqModel.ValueDateFrom ?? DateTime.MinValue,
endDate: reqModel.ValueDateTo ?? YLErp.BLL.valuedateBLL.ValueDate,
IsGetOuterMarginGap: reqModel.IsGetOuterMarginGap);
return JsonSuccess(datas);
}
}
/// <summary>
/// 客户银行卡信息
/// </summary>
[HttpPost, Route("api/v1/clientBankCardList")]
public ApiResponseModel ClientBankCardList([FromBody] ClientInfoQueryModel queryModel)
{
if (queryModel is null)
{
return JsonError("缺少必需的请求参数");
}
var result = new ClientInfoQueryService(CurUser).GetClientBankCards(queryModel);
return JsonSuccess(result);
}
/// <summary>
/// 客户人员信息
/// </summary>
[HttpPost, Route("api/v1/clientDutyList")]
public ApiResponseModel ClientDutyList([FromBody] ClientInfoQueryModel queryModel)
{
if (queryModel is null)
{
return JsonError("缺少必需的请求参数");
}
var result = new ClientInfoQueryService(CurUser).GetClientDuties(queryModel);
return JsonSuccess(result);
}
/// <summary>
/// 客户文件批量导出
/// </summary>
[HttpPost, Route("api/v1/clientFileBatchExport")]
public HttpResponseMessage ClientFileBatchExport([FromBody] ClientInfoQueryModel model)
{
if (model is null)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("参数不能为空", System.Text.Encoding.UTF8, "text/plain")
};
}
try
{
var zipBuffer = new ClientInfoQueryService(CurUser).GetZipFileOfClientFiles(model);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(zipBuffer)
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "客户文件批量导出.zip"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(ex.ToString(), System.Text.Encoding.UTF8, "text/plain")
};
}
}
}
}
@@ -0,0 +1,43 @@
using YLErp.Modules.ManagerAPI.EodModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 日终数据查询API
/// </summary>
//[ManagerAuth]
public class EodQueryController : BaseApiController
{
/// <summary>
/// 日终持仓风险
/// </summary>
[HttpPost, Route("api/v1/eod/position_risks")]
public ApiResponseModel PositionRisks([FromBody] EodPositionRisksQueryApiRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var result = new EodPositionRisksQueryApiService(CurUser).QueryPositionRisks(req);
return JsonSuccess(result);
}
/// <summary>
/// 日终持仓风险_互换
/// </summary>
[HttpPost, Route("api/v1/eod/swap_position_risks")]
public ApiResponseModel SwapPositionRisks([FromBody] EodSwapPositionRisksQueryApiRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var result = new EodPositionRisksQueryApiService(CurUser).QuerySwapPositionRisks(req);
return JsonSuccess(result);
}
}
}
@@ -0,0 +1,73 @@
using Org.BouncyCastle.Ocsp;
using YLErp.Modules.ApiModule;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.ExchangeTradeModule;
using YLErp.Modules.TradeRiskCalcModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 场内交易API
/// </summary>
[ManagerAuth]
public class ExchangeTradeController : BaseApiController
{
/// <summary>
/// 获取实时场内交易持仓数据
/// </summary>
[HttpPost, Route("api/v1/realtimeExchangePositions")]
[Route("api/v1/exchange/realtimeExchangePositions")]
public ApiResponseModel RealtimeExchangePositions()
{
// 获取用户信息
var user = (LoadUserMiddleware.AuthUserInfo)HttpContext.User.Identity;
// 通过接口查询持仓数据
var result = new YLErpWebApiHelper(user.CreateJwtToken).GetExchangePositions().Result;
// 响应结果
return JsonSuccess(result==null?Enumerable.Empty<ExchangePositionItem>(): result);
}
/// <summary>
/// 获取实时场内交易持仓数据
/// </summary>
[HttpPost, Route("api/v1/todayExchangePositions")]
[Route("api/v1/exchange/todayExchangePositions")]
public ApiResponseModel TodayExchangePositions([FromBody] TodayExchangePositionsReqModel req)
{
var date = req?.ValueDate ?? valuedateBLL.ValueDate;
using (var db = DbContextFactory.GetYLDbContext())
{
var tdQuery = from trad in db.ExchangeTrade.AsNoTracking()
where trad.TradeDate == date && trad.IsValid
select trad;
var tdDatas = tdQuery.ToArray();
var results = new ExchangePositionCalcService(date, new ExchangeTradeCommissionCalc()).Calculate(tdDatas, null, null);
return JsonSuccess(results);
}
}
/// <summary>
/// 获取实时场内交易持仓数据
/// </summary>
[HttpPost, Route("api/v1/exchange/trade/save")]
public ApiResponseModel SaveExchangeTrade([FromBody] ExchangeTradeApiReq req)
{
if (req.TradeType == "期货")
{
req.TradeType = "商品期货";
}
var td = new ExchangeTradeApiService(CurUser).Save(req, "WebApi", out var isAddNew);
return JsonSuccess(new
{
td.id,
result = isAddNew ? "成功新增" : "成功更新"
});
}
}
}
@@ -0,0 +1,29 @@
using YLErp.Modules.EodModule.SettlementModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 用于管理端的客户数据API
/// </summary>
[ManagerAuth]
public class FangDunController : BaseApiController
{
/// <summary>
/// 方顿日终数据接口1
/// </summary>
[HttpPost]
[Route("api/v1/Fangdun/EodData1")]
public ApiResponseModel EodData1([FromBody] FangDunEodData1Request req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req), "参数不能为空");
}
var responseTrade = new FangDunSettlementService(CurUser).GetEodData1(req);
return JsonSuccess(responseTrade);
}
}
}
@@ -0,0 +1,46 @@
using YLErp.Modules.EodModule;
using YLErp.Modules.TradeModule.SwapModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
[ManagerAuth]
public class GTJASwapTradeController : BaseApiController
{
/// <summary>
/// 交易流水导入API
/// </summary>
[HttpPost, Route("api/swap/tradeFlow")]
public ApiResponseModel SwapTradeFlow([FromBody] TradeSwapFlowRequestModel model)
{
LogFactory.GetLogger<GTJASwapTradeController>().Info($"SwapTradeFlow");
if (model == null)
{
throw new ServiceException($"参数不符");
}
var result = new SwapTradeFlowMoreImportService(CurUser).SwapTradeFlow(model);
return JsonSuccess(0, "ok");
}
/// <summary>
/// 交易持仓明细API
/// </summary>
[HttpPost, Route("api/swap/eodTrade")]
public ApiResponseModel EodTradeFlow([FromBody] EodSwapTradeRequestDate model)
{
LogFactory.GetLogger<GTJASwapTradeController>().Info($"EodTradeFlow");
if (model == null || model.models.Count() < 1)
{
throw new ServiceException($"参数不符");
}
var result = new EodSwapPositionMannualImportService(CurUser).EodTradeSwap(model);
return JsonSuccess(0, "ok");
}
}
}
@@ -0,0 +1,26 @@
using YLErp.Modules.ScenarioModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 用于内部压力测试的API
/// </summary>
public class InnerTestController : BaseApiController
{
/// <summary>
/// 情景分析压力测试API
/// </summary>
[HttpPost, Route("api/inner/scenaro_calc"), ManagerAuth()]
public ApiResponseModel ScenaroCalc([FromBody] ScenarioRequest reqModel)
{
if (reqModel == null)
{
return JsonError("参数为null");
}
var result = new ScenarioCalcService(CurUser).Calculate(reqModel);
return JsonSuccess(result);
}
}
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Authorization;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 管理端认证属性
/// </summary>
public class ManagerAuthAttribute : AuthorizeAttribute
{
public const string SecretKey = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrkzI1NiJ9eyJjbGFTTT";
public ManagerAuthAttribute()
{
Policy = "manager-side";
AuthenticationSchemes = "manager-side";
}
}
}
@@ -0,0 +1,109 @@
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using System.Text;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 管理端认证token
/// </summary>
public class ManagerTokenController : ControllerBase
{
[Route("api/token")]
public dynamic ManagerToken(ApiTokenRequest request)
{
if (!Request.Headers.TryGetValue("Authorization", out var authHeaders))
{
return new ApiResponseModel { errcode = 1, errmsg = "HTTP Header属性Authorization参数未设置" };
}
var authHeader = authHeaders.FirstOrDefault() ?? string.Empty;
if (!authHeader.StartsWith("Basic "))
{
return new ApiResponseModel { errcode = 1, errmsg = "HTTP Header属性Authorization参数错误" };
}
int index;
string joinStr;
try
{
var bytes = Convert.FromBase64String(authHeader.Substring(6));
joinStr = Encoding.UTF8.GetString(bytes);
index = joinStr.IndexOf(':');
if (index <= 0)
{
return new ApiResponseModel { errcode = 1, errmsg = "HTTP Header属性Authorization参数错误" };
}
}
catch (Exception ex)
{
return new ApiResponseModel { errcode = 1, errmsg = ex.GetBaseException().Message };
}
var userName = joinStr.Substring(0, index);
var password = joinStr.Substring(index + 1);
using (var db = new ErpBaseContext())
{
var sysUser = db.SystemUsers.Where(o => o.State == (int)UserState.Enabled && (o.LoginName == userName))
.Select(n => new SystemUserDto
{
Id = n.Id,
Name = n.Name,
LoginName = n.LoginName,
Password = n.Password,
UserGroup = n.UserGroup
}).FirstOrDefault();
if (sysUser == null)
{
return new ApiResponseModel { errcode = 1, errmsg = $"用户({userName})不存在" };
}
//验证通过
if (!sysUser.CheckPassword(password))
{
return new ApiResponseModel { errcode = 1, errmsg = "验证未通过" };
}
var expireIn = request?.ExpiresIn ?? 7200;
if (expireIn < 10)
{
expireIn = 10;
}
var token = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSerializer(new JsonNetSerializer())
.WithSecret(ManagerAuthAttribute.SecretKey)
.AddClaim("exp", DateTimeOffset.UtcNow.AddSeconds(expireIn).ToUnixTimeSeconds())
.AddClaim("userId", sysUser.Id)
.AddClaim("userName", sysUser.Name.TrimToNull() ?? sysUser.LoginName)
.AddClaim("userGroup", sysUser.UserGroup)
.Encode();
return new ApiTokenResponseModel
{
tokenType = "Bearer",
accessToken = token,
expiresIn = expireIn,
userId = sysUser.Id,
userName = sysUser.Name,
userGroup = sysUser.UserGroup
};
}
}
}
public class ApiTokenRequest
{
/// <summary>
/// 有效时间(秒)
/// </summary>
public int? ExpiresIn { get; set; }
}
}
@@ -0,0 +1,117 @@
using YLErp.Model.Enum;
using YLErp.Modules.TradeModule.OrderModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易下单控制器
/// </summary>
[ManagerAuth]
public class OrderApiController : BaseApiController
{
/// <summary>
/// 场外期权交易
/// </summary>
[HttpPost, Route("api/v1/order/option")]
public ApiResponseModel OrderOption([FromBody] OtcOptionTradeFullEx model, bool debug = false)
{
var trade = new OtcTradeSaveService(CurUser).SaveOptionTradeFromApiOrImport(model, TradeSourceEnum.WebApiV2);
if (debug)
{
return JsonSuccess(trade, "保存成功");
}
return JsonSuccess(trade.TradeNumber, "保存成功");
}
/// <summary>
/// 场外期权结构化交易
/// </summary>
[HttpPost, Route("api/v1/order/structure_options")]
public ApiResponseModel OrderStructure([FromBody] StructureOrderRequest model, bool debug = false)
{
var trade = new OtcTradeSaveService(CurUser).SaveStructureOptionTradeFromApiOrImport(model, TradeSourceEnum.WebApiV2);
if (debug)
{
return JsonSuccess(trade, "保存成功");
}
return JsonSuccess(trade.TradeNumber, "保存成功");
}
/// <summary>
/// 场外期权组合交易
/// </summary>
[HttpPost, Route("api/v1/order/group_options")]
public ApiResponseModel OrderGroup([FromBody] StructureOrderRequest model, bool debug = false)
{
var trade = new OtcTradeSaveService(CurUser).SaveGroupOptionTradeFromApiOrImport(model, TradeSourceEnum.WebApiV2);
if (debug)
{
return JsonSuccess(trade, "保存成功");
}
return JsonSuccess(trade.TradeNumber, "保存成功");
}
/// <summary>
/// 场外期权组合交易--修改主交易
/// </summary>
[HttpPost, Route("api/v1/order/group_options_main_update")]
public ApiResponseModel OrderGroupEdit([FromBody] GroupTradeMainEditRequest model, bool debug = false)
{
var td = new OtcOptionTradeFullEx
{
_Update = true,
TradeNumber = model.TradeNumber,
ExerciseDate = model.ExerciseDate,
BuySell = model.BuySell,
StructureType = model.StructureType,
Comments = model.Comments,
IsMoneynessOption = model.IsMoneynessOption,
AssetBookName = model.AssetBookName,
TraderName = model.TraderName,
ExtendInfo = model.ExtendInfo,
OptionType = model.OptionType,
NotKIParticipationRate = model.NotKIParticipationRate,
BarrierLow = model.BarrierLow ?? 0,
Strike = model.Strike,
HighStrike = model.HighStrike,
HasPayoffLimit = model.HasPayoffLimit,
KIParticipationRate = 1,
SpotPrice = model.SpotPrice
};
if (model.StockEqvNotional.HasValue)
{
td.IsUsePremiumRate = true;
td.StockEqvNotional = td.StockEqvNotionalReal = model.StockEqvNotional.Value;
}
else if (model.TradeAmount.HasValue)
{
td.IsUsePremiumRate = false;
td.TradeAmount = model.TradeAmount.Value;
}
var trade = new OtcTradeSaveService(CurUser).SaveOptionTradeFromApiOrImport(td, TradeSourceEnum.WebApiV2);
if (debug)
{
return JsonSuccess(trade, "保存成功");
}
return JsonSuccess(trade.TradeNumber, "保存成功");
}
/// <summary>
/// 远期录入api
/// </summary>
[HttpPost, Route("api/v1/order/ForwardTradeSave")]
public ApiResponseModel ForwardTradeSave([FromBody] OtcTradeForward req, bool debug = false)
{
var trade = new OtcTradeSaveService(CurUser).SaveOptionForwardTradeFromApi(req, TradeSourceEnum.WebApiV2);
if (debug)
{
return JsonSuccess(trade, "保存成功");
}
return JsonSuccess(trade.TradeNumber, "保存成功");
}
}
}
@@ -0,0 +1,74 @@
using YLErp.Modules.TradeModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 场外期权交易API
/// </summary>
[ManagerAuth]
public class OtcOptionTradeController : BaseApiController
{
/// <summary>
/// 保存场外期权对冲波动率
/// </summary>
[HttpPost, Route("api/v1/saveOtcTradeHedgingVol"), ManagerAuth]
public ApiResponseModel SaveTradeHedgingVol([FromBody] TradeHisDataUpdateRequest req)
{
if (req is null)
{
return JsonError("缺少必要的输入参数");
}
if (req.Value < 0)
{
return JsonError("请设置合理的对冲波动率");
}
new TradeHisDataService(CurUser).SaveHedgeVol(req.TradeNumber, valuedateBLL.ValueDate, req.Value);
return JsonSuccess("成功");
}
/// <summary>
/// 更新场外期权无风险利率
/// </summary>
[HttpPost, Route("api/v1/otc-option/trade/updateRiskFreeRate")]
public ApiResponseModel UpdateRiskFreeRate([FromBody] TradeHisDataUpdateRequest req)
{
if (req is null)
{
return JsonError("缺少必要的输入参数");
}
if (req.Value < 0)
{
return JsonError("请设置合理的无风险利率");
}
new TradeHisDataService(CurUser).SaveNoRiskRate(req.TradeNumber, valuedateBLL.ValueDate, req.Value);
return JsonSuccess("成功");
}
/// <summary>
/// 更新场外期权分红率
/// </summary>
[HttpPost, Route("api/v1/otc-option/trade/updateDividendRate")]
public ApiResponseModel UpdateDividendRate([FromBody] TradeHisDataUpdateRequest req)
{
if (req is null)
{
return JsonError("缺少必要的输入参数");
}
if (req.Value < 0)
{
return JsonError("请设置合理的分红率");
}
new TradeHisDataService(CurUser).SaveDividendRate(req.TradeNumber, valuedateBLL.ValueDate, req.Value);
return JsonSuccess("成功");
}
}
}
@@ -0,0 +1,40 @@
using YLErp;
using YLErp.Modules.ApprovalModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
public class ProcessLogController : BaseApiController
{
/// <summary>
/// 审批结果变更
/// <para>供天风OA调用</para>
/// <param name="type">操作类型,可选值为1:交易审批;2:客户审批;3:资金审批;</param>
/// <param name="code">交易编号/客户编号/资金编号</param>
/// <param name="status">审批状态,true:通过;false:拒绝;</param>
/// <param name="remark">备注信息</param>
/// </summary>
[HttpPost, Route("api/v1/ApprovalResult")]
public ApiResponseModel ApprovalResult([FromBody] ApprovalResult obj)
{
if (PS.Config.ErpElement.InterfaceApprovalMode <= 0)
{
return JsonError("接口审批功能未开启,请联系管理员!");
}
if (obj == null)
{
return JsonError("参数错误");
}
if (obj.type < 1 || obj.type > 3)
{
return JsonError("未知的操作类型");
}
obj.code = obj.code ?? "";
var userInfo = new OptUserInfo(0,"审批接口", OptUserFrom.WebAPI);
new ApprovalService(userInfo).ApprovalResult(obj, out var msg);
return JsonSuccess(msg);
}
}
}
@@ -0,0 +1,81 @@
using Newtonsoft.Json;
using YLErp.BLL.EodSettlement;
using YLErp.Modules.IntradayModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 实时结算数据
/// </summary>
[ManagerAuth]
public class RealTimeController : BaseApiController
{
/// <summary>
/// 实时结算数据
/// </summary>
[HttpPost, Route("api/v1/realtime/info_all")]
public IActionResult RealTimeSettlementInfoAll([FromBody] RealTimeSettlementInfoAllRequest req)
{
//当前交易日期
var valueDate = YLErp.BLL.valuedateBLL.ValueDate;
var startDate = req?.StartDate ?? valueDate;
if (startDate > valueDate)
{
return Ok(JsonError("处理请求失败:开始日期不能大于当天"));
}
var context = new IntradaySettleInfoQueryContext(CurUser);
var clientIdsOfInside = context.GetClientIdsOfInside();
var clientIdsOfNoInside = context.GetClientIds().Where(a => !clientIdsOfInside.Contains(a)).ToList();
//持仓记录
var positionResult = new IntradayPositionApiService(context).GetIntradayPositions(valueDate, clientIdsOfInside);
//平仓记录
var CloseInfos = new IntradayCloseInfoQueryService(context).GetIntradayCloseInfoList(valueDate, clientIdsOfInside);
//账户结算信息
var AccountInfos = ClientBalanceUtility.GetClientBanlances(
clientIdList: clientIdsOfNoInside,
startDate: startDate,
endDate: valueDate,
IsGetOuterMarginGap: false,
IsClientBalanceGap: false,
ParentFlag: false,
clientDataSource: context);
//账户基础信息
var AccountBaseInfos = context.AsQueryable().Where(a => !clientIdsOfInside.Contains(a.id)).Select(n => new { n.id, n.ParentId, n.Name, n.Number, n.LicenseCode }).ToArray();
var SummaryInfo = new IntradayPositionApiService(context).GetClientSettleBalancesSumInfo(AccountInfos);
var result = new
{
positionResult.OptionPositions,
positionResult.SwapPositions,
positionResult.ForwardPositions,
CloseInfos,
AccountInfos,
AccountBaseInfos,
SummaryInfo
};
var serializeSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
if (serializeSettings.Converters == null)
{
serializeSettings.Converters = new List<JsonConverter>();
}
serializeSettings.Converters.Add(new DoubleJsonWriteConverter(2, 8));
return new JsonResult(new ApiResponseModel { data = result }, serializeSettings);
}
}
}
@@ -0,0 +1,181 @@
using Newtonsoft.Json;
using SharedWebApi.Filter;
using YLErp;
using YLErp.BLL.Eod;
using YLErp.BLL.EodSettlement;
using YLErp.Modules.EodModule.QueryModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 清算API
/// </summary>
[ManagerAuth]
public class SettlementController : BaseApiController
{
/// <summary>
/// 日终结算信息
/// </summary>
[HttpPost, Route("api/v1/settlement/ready_info")]
public ApiResponseModel ReadyInfo([FromBody] SettlementReadyInfoReuqest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var lastDate = EodOperationBase.GetLastSettlementDate(req.ValueDate ?? YLErp.BLL.valuedateBLL.ValueDate);
var eodstatus = YLErp.Modules.DbContextFactory.GetYLDbContext().eodStatus.Where(x => x.ValueDate == lastDate)?.FirstOrDefault();
var info = new SettlementReadyInfo
{
LastSettlementDate = lastDate.ToString("yyyy-MM-dd"),
CheckStatus = eodstatus?.CheckStatus ?? false
};
return JsonSuccess(info);
}
/// <summary>
/// 日终清算数据
/// </summary>
//[TypeFilter(typeof(ActionLogFilter))]
[HttpPost, Route("api/v1/settlement/info_all")]
public IActionResult SettlementInfoAll([FromBody] EodSettleInfoRequest req)
{
if (req is null)
{
return Ok(JsonError("处理请求失败:参数为null"));
}
var context = new EodSettleInfoQueryContext(CurUser);
var clientIdsOfInside = context.GetClientIdsOfInside();
var clientIdsOfNoInside = context.GetClientIds().Where(a => !clientIdsOfInside.Contains(a)).ToList();
//持仓记录
var positionResult = new EodTradePositionApiService(context).GetEodPositions(req, clientIdsOfInside);
//平仓记录
var CloseInfos = new EodCloseInfoQueryService(context).GetEodCloseInfoList(req.ValueDate, clientIdsOfInside);
//账户结算信息
var AccountInfos = ClientBalanceUtility.GetClientBanlances(
clientIdList: clientIdsOfNoInside,
startDate: DateTime.MinValue,
endDate: req.ValueDate,
IsGetOuterMarginGap: false,
IsClientBalanceGap: false,
ParentFlag: false,
clientDataSource: context);
//账户基础信息
var AccountBaseInfos = context.AsQueryable().Where(a => !clientIdsOfInside.Contains(a.id)).Select(n => new { n.id, n.ParentId, n.Name, n.Number, n.LicenseCode }).ToArray();
var SummaryInfo = new EodTradePositionApiService(context).GetClientSettleBalancesSumInfo(AccountInfos);
var result = new
{
positionResult.OptionPositions,
positionResult.SwapPositions,
positionResult.ForwardPositions,
CloseInfos,
AccountInfos,
AccountBaseInfos,
SummaryInfo
};
var serializeSettings = new JsonSerializerSettings
{
NullValueHandling = req.CompactResult ? NullValueHandling.Ignore : NullValueHandling.Include
};
if (serializeSettings.Converters == null)
{
serializeSettings.Converters = new List<JsonConverter>();
}
serializeSettings.Converters.Add(new DoubleJsonWriteConverter(2, 8));
var bakFolder = OtcAppContext.MapPath("/log/settlement_info_all");
Task.Run(() =>
{
try
{
Directory.CreateDirectory(bakFolder);
var json = JsonHelper.Serialize(result);
System.IO.File.WriteAllText(Path.Combine(bakFolder, DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".json"), json);
}
catch (Exception ex)
{
LogFactory.GetLogger("settlement_info_all_bak").Error(ex);
}
});
return new JsonResult(new ApiResponseModel { data = result }, serializeSettings);
}
/// <summary>
/// 日终清算--持仓列表
/// </summary>
[HttpPost, Route("api/v1/settlement/position_all")]
public ApiResponseModel SettlementPositionAll([FromBody] EodSettleInfoRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var context = new EodSettleInfoQueryContext(CurUser);
var result = new EodTradePositionApiService(context).GetEodPositions(req);
return JsonSuccess(result);
}
/// <summary>
/// 日终清算--平仓流水
/// </summary>
[HttpPost, Route("api/v1/settlement/closeInfo_all")]
public ApiResponseModel CloseInfoAll([FromBody] EodSettleInfoRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var context = new EodSettleInfoQueryContext(CurUser);
var closeResult = new EodCloseInfoQueryService(context).GetEodCloseInfoList(req.ValueDate);
return JsonSuccess(closeResult);
}
/// <summary>
/// 日终清算--平仓流水
/// </summary>
[HttpPost, Route("api/v1/settlement/accountInfo_all")]
public ApiResponseModel ClientAccountInfoAll([FromBody] EodSettleInfoRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
var context = new EodSettleInfoQueryContext(CurUser);
var clientIds = context.GetClientIds();
var balanceResult = ClientBalanceUtility.GetClientBanlances(
clientIdList: clientIds,
startDate: System.DateTime.MinValue,
endDate: req.ValueDate,
IsGetOuterMarginGap: false,
IsClientBalanceGap: false,
ParentFlag: false,
clientDataSource: context);
return JsonSuccess(balanceResult);
}
}
}
@@ -0,0 +1,36 @@
using YLErp;
using YLErp.Modules.DataCacheModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
[ManagerAuth]
public class SysInfoController : BaseApiController
{
[HttpPost, Route("api/sysinfo/index")]
public ApiResponseModel Index()
{
var info = new
{
= AppManager.Version,
= YLErp.Modules.TradeRiskCalcModule.TradeRiskCalcTaskRunner.ValueDate,
sysinfo = AppManager.GetSysInfo(),
DataTrace_MaxID = DataCacheManager.MaxId
};
return JsonSuccess(info);
}
[HttpPost, Route("api/sysinfo/DataCache/tables")]
public ApiResponseModel DataCache_Tables()
{
var tables = DataCacheManager.GetSinkTables();
return JsonSuccess(tables);
}
[HttpPost, Route("api/sysinfo/DataCache/data")]
public ApiResponseModel DataCache_Data([FromQuery] string tableName)
{
var data = DataCacheManager.GetSinkData(tableName);
return JsonSuccess(data);
}
}
}
@@ -0,0 +1,36 @@
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 系统API
/// </summary>
public class SystemController : BaseApiController
{
/// <summary>
/// 系统信息
/// </summary>
[HttpPost, Route("api/m/system_info")]
public ApiResponseModel SystemInfo()
{
var info = new
{
YLErp.AppManager.Version,
ValueDate = valuedateBLL.ValueDate.ToString("yyyy-MM-dd"),
RiskFreeRate = valuedateBLL.SysRiskFreeRate(),
};
return JsonSuccess(info);
}
/// <summary>
/// 无风险利率查询(从利率曲线设置中获取无风险利率)
/// </summary>
[HttpPost]
[Route("api/v1/riskFreeRate")]
public ApiResponseModel RiskFreeRate([FromBody] RiskFreeRateRequest req)
{
var riskFreeRate = valuedateBLL.GetRiskFreeRateFromCurve(req?.StartDate, req?.EndDate);
return JsonSuccess(riskFreeRate / 100);
}
}
}
@@ -0,0 +1,97 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
using YLErp.Modules.ApiModule.ExportModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易合约文档
/// </summary>
[ManagerAuth]
public class TradeContractDocController : BaseApiController
{
/// <summary>
/// 交易确认书批量导出API
/// </summary>
[HttpPost, Route("api/v1/otc/trade_contract/OpenDocBatchExport")]
public HttpResponseMessage OtcTradeContractOpenDocExport([FromBody] OtcTradeContractOpenDocExportRequest model)
{
if (model is null)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("参数不能为空", System.Text.Encoding.UTF8, "text/plain")
};
}
try
{
var zipBuffer = new OtcTradeContractDocExportService(CurUser).GetZipFileOfOpenDocs(model, out _);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(zipBuffer)
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName =HttpUtility.UrlEncode("交易确认书批量导出.zip",Encoding.UTF8)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(ex.ToString(), System.Text.Encoding.UTF8, "text/plain")
};
}
}
/// <summary>
/// 了结确认书批量导出API
/// </summary>
[HttpPost, Route("api/v1/otc/trade_contract/CloseDocBatchExport")]
public HttpResponseMessage OtcTradeContractCloseDocExport([FromBody] OtcTradeContractCloseDocExportRequest model)
{
if (model is null)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("参数不能为空", System.Text.Encoding.UTF8, "text/plain")
};
}
try
{
var zipBuffer = new OtcTradeContractDocExportService(CurUser).GetZipFileOfCloseDocs(model, out _);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(zipBuffer)
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = HttpUtility.UrlEncode("了结确认书批量导出.zip", Encoding.UTF8)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(ex.ToString(), System.Text.Encoding.UTF8, "text/plain")
};
}
}
}
}
@@ -0,0 +1,158 @@
using YLErp.DBModels.Consts;
using YLErp.Modules.EodModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.ApiModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Modules.TradeModule.OrderModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易操作
/// </summary>
[ManagerAuth]
public class TradeDealController : BaseApiController
{
/// <summary>
/// 交易了结
/// </summary>
[HttpPost, Route("api/v1/tradeClose")]
public ApiResponseModel TradeClose(TradeCloseRequestModel model)
{
if (model is null)
{
return JsonError("处理请求失败:参数为null");
}
//以后转为前后端分离模式后还需要重新处理
model.ImportFlag = "WebAPI";
var result = new OtcTradeCloseService(CurUser).ExecuteClose(model);
var td = result.Trade;
var tc = result.TradeCash;
var Notional = td.Notional;
if (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus))
{
Notional = 0;
}
var TcAction = tc.ExerciseWay == TradeCashExerciseWayEnum. ? "到期" : "终止";
var TcTradePrice = (td.TradePrice ?? 0) * (tc.UnwindPercentRate ?? 0) * (td.BuySell == "买入" ? -1 : 1);
var WinLoss = tc.Amount + TcTradePrice;
//参考OtcTradeDetail类
var ret = new
{
td.TradeNumber,
td.TradeStatus,
Notional,
TcAction,
TcValueDate = tc.HappenedDate ?? tc.ValueDate,
TcFinalPrice = tc.FinalPrice,
TcUnwindPrice = tc.UnwindPrice,
TcUnwindPricePercent = tc.UnwindPricePercentRate,
TcUnwindNotional = tc.UnwindNotional ?? tc.Notional,
TcUnwindPercent = tc.UnwindPercentRate,
TcAmount = tc.Amount,
WinLoss,
TcTradePrice
};
return JsonSuccess(ret);
}
/// <summary>
/// 组合交易主交易了结
/// </summary>
[HttpPost, Route("api/v1/trade/group_options_close_start")]
public ApiResponseModel TradeGroupClose([FromBody] TradeGroupCloseRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
new TradeGroupCloseService(CurUser).ExecuteCloseStart(req);
return JsonSuccessInfo("操作成功");
}
/// <summary>
/// 完成组合交易了结
/// </summary>
[HttpPost, Route("api/v1/trade/group_options_close_complete")]
public ApiResponseModel FinishGroupTradeCash([FromBody] TradeGroupCloseRequest req)
{
using (var DbContext = YLErp.Modules.DbContextFactory.GetYLDbContext())
{
var trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == req.TradeNumber);
if (trade == null)
{
return JsonError("未找到该交易编号对应的交易");
}
var tradeCash = DbContext.trade_cash.OrderByDescending(x => x.id).FirstOrDefault(x => x.TradeId == trade.id && x.ValidState != "InValid" && x.Status == null && x.Action != "系统操作-期权费");
if (tradeCash == null)
{
return JsonError("未找到该组合交易未完成的了结记录");
}
new TradeUnwindService(CurUser).FinishGroupTradeCash(tradeCash.id);
return JsonSuccessInfo("已完成");
}
}
/// <summary>
/// 期权交易-设置日终观察价格
/// </summary>
[HttpPost, Route("api/v1/trade/option/setObservationPrice")]
public ApiResponseModel TradeSetObservationPrice(SetObservationPriceApiRequest req)
{
if (req is null)
{
return JsonError("处理请求失败:参数为null");
}
new ManuallyTradeObservationPriceService(CurUser).SetObservationPriceApi(req);
return JsonSuccess();
}
/// <summary>
/// 撤回交易了结审核
/// </summary>
[HttpPost, Route("api/v1/trade/withdrawCloseApproval")]
public ApiResponseModel WithdrawCloseApproval(SingleTradeQueryModel model)
{
if (model is null)
{
return JsonError("处理请求失败:参数为null");
}
new TradeRevokeService(CurUser).TradeRevoke(model.TradeNumber);
return JsonSuccessInfo("成功");
}
/// <summary>
/// 交易无效操作
/// </summary>
[HttpPost, Route("api/v1/trade/invalid")]
public ApiResponseModel TradeInvalid(TradeInvalidRequest request)
{
if (request is null)
{
return JsonError("缺少必要的输入参数");
}
new TradeInvalidService(CurUser).InvalidTrade(request.TradeNumber, true);
return JsonSuccess("成功");
}
}
}
@@ -0,0 +1,105 @@
using YLErp.Enums;
using YLErp.Modules.ApiModule.PricingModule;
using YLErp.Modules.ApiModule.PricingModule.CustomizedAsianPricing;
using YLErp.Modules.PricingModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易定价API
/// </summary>
public class TradePricingController : BaseApiController
{
/// <summary>
/// 期权定价
/// </summary>
[HttpPost, Route("api/v1/optionPricing"), ManagerAuth]
public ApiResponseModel optionPricing2([FromBody] OptionPricingModelV1 reqModel)
{
if (reqModel == null)
{
return JsonError("参数为null");
}
var result = new PriceCalcService(CurUser).CalcOptionPrice(reqModel.Trades, reqModel.IsCalcMargin, CalcScenarioEnum.Pricing, td => reqModel.IsCalcGreeks);
return JsonSuccess(result);
}
/// <summary>
/// 期权定价
/// </summary>
[HttpPost, Route("api/v1/otc-option/pricing"), ManagerAuth]
public ApiResponseModel OtcOptionPricing([FromBody] OptionPricingModelV2 req)
{
if (req is null)
{
return JsonError("参数不能为空");
}
var result = new OptionPricingApiService().GetOptionPrice(req);
return JsonSuccess(new
{
req.CalcId,
InitialMargin = result.initialMargin,
ContractSize = result.contractSize,
result.calcResult.Pv,
result.calcResult.Delta,
result.calcResult.Gamma,
result.calcResult.Vega,
result.calcResult.Theta,
result.calcResult.Rho
});
}
/// <summary>
/// 保险加期货等奇异但不太常用的奇异期权定价,如各种亚式变种等
/// </summary>
[HttpPost, Route("api/v1/otc-option/customized_asian_pricing"), ManagerAuth]
public ApiResponseModel CustomizedAsianPricing([FromBody] CustomizedAsianPricingModel req)
{
if (req is null)
{
return JsonError("参数不能为空");
}
if (req.ExoticAsianType == "触碰锁定增强亚式期权") // 宏源恒利的保险加期货产品2022
{
var result = new OptionPricingApiService().GetCustomizedAsianOptionPrice(req);
return JsonSuccess(new
{
req.CalcId,
InitialMargin = result.initialMargin,
ContractSize = result.contractSize,
result.calcResult.Pv,
result.calcResult.Delta,
result.calcResult.Gamma,
result.calcResult.Vega,
result.calcResult.Theta,
result.calcResult.Rho
});
}
else
{
return JsonError("不支持的期权类型");
}
}
/// <summary>
/// 雪球期权反算票息
/// </summary>
[HttpPost, Route("api/v1/otc-option/snowball/calc_korebate"), ManagerAuth]
public ApiResponseModel CalcSnowballKoRebate([FromBody] OptionPricingModelV2 req)
{
OptionPricingApiHelper.CheckPricingRequestData(req);
req.TradeType = "雪球期权";
var korebate = new PriceCalcService(CurUser).CalcSnowballKORebate(req);
return JsonSuccess(korebate);
}
}
}
@@ -0,0 +1,47 @@
using YLErp.Modules.StructureModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易公共信息查询
/// </summary>
[ManagerAuth]
public class TradePublicInfoQueryController : BaseApiController
{
/// <summary>
/// 交易明细查询API
/// </summary>
[HttpPost, Route("api/v1/getCustomStructureDefination"), ManagerAuth()]
public ApiResponseModel CustomStructureDefination(CustomStructureDefinationReq req)
{
LogFactory.GetLogger<TradePublicInfoQueryController>().Info($"StructureType:{req.StructureType}");
var dict = new StructureService(CurUser).QueryStructureMap(YLErp.Enums.StructureRangeEnum.CUSTOM_TRADE);
if (!string.IsNullOrWhiteSpace(req.StructureType))
{
dict = dict.Where(O => O.Key == req.StructureType).ToDictionary(K => K.Key, V => V.Value);
}
var result = dict.Select(O => new
{
StructureType = O.Key,
Fields = O.Value.Select(B =>
{
var Type = EnumHelper.GetDescriptionByName(B.ColumnType);
Type = Type == "下拉列表" ? "文本" : Type;
return new
{
Name = B.ColumnName,
Type
};
}
).ToArray()
});
LogFactory.GetLogger<TradePublicInfoQueryController>().Info($"StructureType:{result.ToJson()}");
return JsonSuccess(result, "ok");
}
public class CustomStructureDefinationReq
{
public string StructureType { get; set; }
}
}
}
@@ -0,0 +1,162 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.ApiModule;
using YLErp.Modules.TradeModule.BaseModels;
using YLErp.Modules.TradeModule.QueryModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// OTC-13931 交易明细API接口
/// </summary>
[ManagerAuth]
public class TradeQueryController : BaseApiController
{
/// <summary>
/// 交易明细查询API
/// </summary>
[HttpPost, Route("api/v1/tradeDetailList")]
public ApiResponseModel TradeDetailList([FromBody] TradeDetailQueryApiModel model)
{
if (model is null)
{
model = new TradeDetailQueryApiModel();
}
model.IncludeGroupMain = true;
var pagedList = new TradeDetailQueryApiService(CurUser).TradeDetailPageList(model);
return JsonPagedList(pagedList);
}
/// <summary>
/// 交易明细查询API
/// </summary>
[HttpPost, Route("api/v2/tradeDetailList")]
public ApiResponseModel TradeDetailListV2([FromBody] TradeDetailQueryApiV2Request request)
{
if (request is null)
{
request = new TradeDetailQueryApiV2Request();
}
request.IncludeGroupMain = true;
var list = new TradeDetailQueryApiV2Service(CurUser).GetList(request);
return JsonSuccess(list);
}
/// <summary>
/// 当日所有了结信息查询 API
/// </summary>
/// <param name="detailQueryApiModel"></param>
/// <returns></returns>
[HttpPost, Route("api/v1/tradeCloseInfoList")]
public ApiResponseModel TradeCloseInfoList([FromBody] TradeCloseInfoRequestModel model)
{
if (model is null)
{
model = new TradeCloseInfoRequestModel();
}
if (model.ValueDate == null)
{
return JsonError($"了结日期 参数必须赋值");
}
var result = new TradeCloseInfoQueryApiService(CurUser).GetTradeCloseInfoList(model);
return JsonSuccess(result);
}
/// <summary>
/// 根据了结日期,交易编号获取交易了结信息,可以不传参数即获取所有了结信息
/// </summary>
[HttpPost, Route("api/v1/tradeCloseInfos")]
public ApiResponseModel TradeCloseInfos(TradeSettleQueryModel model)
{
if (model is null)
{
return JsonError("处理请求失败:参数为null");
}
var infos = new OtcOptionMultiCloseService(CurUser).GetTradeCloseInfos(model.TradeNumber, "全部", model.ValueDate);
return JsonSuccess(infos);
}
/// <summary>
/// 交易列表导出API
/// </summary>
[HttpPost, Route("api/v1/otc-option/tradeListExport")]
public HttpResponseMessage OtcOptionTradeExport([FromBody] OtcOptionTradeExportRequest model)
{
if (model is null)
{
model = new OtcOptionTradeExportRequest();
}
try
{
var bytes = new OtcOptionTradeExportService().ExportToExcel(model);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(bytes)
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName =HttpUtility.UrlEncode( $"场外期权成交记录-{DateTime.Now:yyyy-MM-dd}.xlsx",Encoding.UTF8)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(ex.ToString(), System.Text.Encoding.UTF8, "text/plain")
};
}
}
/// <summary>
/// 场外期权交易开平仓信息 API
/// </summary>
[HttpPost, Route("api/v1/otc-option/open_close_infos")]
public ApiResponseModel TradeOpenCloseInfos([FromBody] SingleTradeQueryModel model)
{
if (model is null)
{
return JsonError("处理请求失败:参数为null");
}
if (string.IsNullOrEmpty(model.TradeNumber))
{
return JsonError("请填写交易编号");
}
var openInfo = new TradeDetailsQueryService(CurUser).GetOtcTradeFull(model.TradeNumber);
if (openInfo == null)
{
return JsonError("没有找到交易开仓信息");
}
openInfo.Notional = openInfo.OriginalNotional ?? 0;
openInfo.StockEqvNotional = openInfo.OriginalStockEqvNotional ?? 0;
var countRatio = YLErp.Modules.DataCacheProvider.GetUnderlyingDataSource().GetCountRatio(openInfo.UnderlyingCode);
openInfo.TradeAmount = openInfo.Notional / countRatio;
openInfo.MetaDic = new TradeMetaService(CurUser).GetTradeMeta(openInfo.id);
var closeInfos = new OtcOptionMultiCloseService(CurUser).GetTradeCloseInfos(model.TradeNumber, "全部");
return JsonSuccess(new
{
OpenInfo = openInfo,
CloseInfos = closeInfos
});
}
}
}
@@ -0,0 +1,252 @@
using Microsoft.AspNetCore.Authentication;
using YLErp.Modules.ApiModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.ScenarioModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeRiskCalcModule;
using YLErp.QdpModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 交易风险API
/// </summary>
[ManagerAuth]
public class TradeRiskCalcController : BaseApiController
{
/// <summary>
/// 实时风险运行切换(用于内部)
/// </summary>
[HttpPost, Route("api/v100/traderisk_switch"), ManagerAuth]
public ApiResponseModel TradeRiskCalcSwitch()
{
AppContext.TryGetSwitch("DisableTradeRiskCalcTaskRunner", out var disable);
disable = !disable;
AppContext.SetSwitch("DisableTradeRiskCalcTaskRunner", disable);
return JsonSuccess(disable ? "停用实时风险计算" : "启用实时风险计算", "text/plain");
}
/// <summary>
/// 实时风险(用于内部)
/// </summary>
[HttpPost, Route("api/v100/traderisk"), ManagerAuth]
public ApiResponseModel TradeRiskCalc([FromBody] TradingRiskReqModel req)
{
if (req == null)
{
req = new TradingRiskReqModel();
}
if (string.IsNullOrWhiteSpace(req.VolType))
{
req.VolType = "对冲";
}
var result = new TradingRiskReqService(CurUser).GetResult(req);
if (result == null)
{
return JsonSuccess(new { rows = Enumerable.Empty<TradingRiskParameter>(), date = "" });
}
return JsonSuccess(new
{
rows = result.TradeRiskList,
date = result.ValueDate.ToString("yyyy-MM-dd"),
calctimes = new[] { result.CalcStartTime, result.CalcEndTime }
});
}
/// <summary>
/// 价格试算(用于内部)
/// </summary>
[HttpPost, Route("api/v100/traderisk_pricecalc"), ManagerAuth]
public ApiResponseModel TradeRiskCalcPriceCalc([FromBody] TradingRiskPriceCalcReqModel req)
{
if (req is null)
{
req = new TradingRiskPriceCalcReqModel();
}
if (string.IsNullOrWhiteSpace(req.VolType))
{
req.VolType = "对冲";
}
var result = new TradingRiskReqService(CurUser).GetPriceCalcResult(req);
if (result == null)
{
return JsonSuccess(new { rows = Enumerable.Empty<TradingRiskParameter>(), date = "" });
}
return JsonSuccess(new { rows = result.TradeRiskList, date = result.ValueDate.ToString("yyyy-MM-dd") });
}
/// <summary>
/// 用正常验证方式提供实时风险数据
/// </summary>
[HttpPost, Route("api/v1/realtimeRiskCalc")]
public ApiResponseModel RealtimeRiskCalc([FromBody] TradingRiskReqModel req)
{
if (req is null)
{
req = new TradingRiskReqModel();
}
req.IncludeStock = true;
//return TradeRiskCalc(req);
// 获取用户信息
var user = (LoadUserMiddleware.AuthUserInfo)HttpContext.User.Identity;
// 通过接口查询风险数据
var result = new YLErpWebApiHelper(user.CreateJwtToken).GetTradingRisk(req).Result;
// 响应结果
if (result == null)
{
return new ApiResponseModel(new { rows = Enumerable.Empty<TradingRiskParameter>(), date = "" });
}
return new ApiResponseModel(new
{
rows = result.TradeRiskList,
date = result.ValueDate.ToString("yyyy-MM-dd"),
calctimes = new[] { result.CalcStartTime, result.CalcEndTime },
message = result.Message
});
}
/// <summary>
/// 提供实时预付金数据
/// </summary>
[HttpPost, Route("api/v1/realtimeMargin")]
public ApiResponseModel RealtimeMargin()
{
var datas = new RealTimeMarginService(CurUser).GetRealTimeMarginResult();
return JsonSuccess(datas);
}
/// <summary>
/// 保存场外期权持仓波动率
/// </summary>
[HttpPost, Route("api/v1/saveOptionTradeVol")]
public ApiResponseModel SaveOptionTradeVol([FromBody] TradeVolatilityDto req)
{
new TradeHisDataService(CurUser).SaveTradeVolatility(req, false);
return JsonSuccess();
}
/// <summary>
/// 历史交易统计数据
/// </summary>
[HttpPost, Route("api/v1/tradePnlStaticsData")]
public IActionResult TradePnlStaticsData()
{
var valueDate = QdpCalendarHelper.GetNonHoliday(DateTime.Today);
var result = TradePnlStaticsDataProvider.Default.GetDatas(valueDate);
return Ok(new { date = valueDate, data = result });
}
/// <summary>
/// 自定义交易风险
/// </summary>
[HttpPost, Route("api/v1/updateCustomTradeRisk")]
public ApiResponseModel UpdateCustomTradeRisk([FromBody] eod_trade_risk_manualDto req)
{
new EodTradeRiskManualService(CurUser).SaveEodTradeRiskManualAPI(req);
return JsonSuccess();
}
/// <summary>
/// 查询交易风险信息
/// </summary>
/// <param name="TradeNumber">交易编号</param>
/// <param name="ValueDate">估值日期</param>
/// <param name="DataType">
/// 数据类型:
/// <para>0:自定义维护数据</para>
/// <para>1:收盘价+持仓波动率</para>
/// <para>2:收盘价+对冲波动率</para>
/// <para>3:收盘价+开仓波动率</para>
/// <para>4:结算价+持仓波动率</para>
/// <para>5:结算价+对冲波动率</para>
/// <para>6:结算价+开仓波动率</para>
/// </param>
/// <returns></returns>
[HttpPost, Route("api/v1/queryTradeRisk")]
public ApiResponseModel QueryTradeRisk([FromBody] QueryTradeRiskReq req)
{
IEnumerable<EodTradeRisk> list;
Dictionary<int, string> tradeNumberDict;
switch (req.DataType)
{
case 1:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
case 2:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk_hedgevol>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
case 3:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk_openvol>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
case 4:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk_s>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
case 5:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk_hedgevol_s>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
case 6:
list = new EodPositionRisksQueryService(CurUser).SearchEodRisk<eod_trade_risk_openvol_s>(req.TradeNumbers, req.ValueDate, out tradeNumberDict).Cast<EodTradeRisk>();
break;
default:
return JsonError($"“{nameof(req.DataType)}”超出可选值范围:(0~6)");
}
var result = new List<QueryTradeRisk>();
foreach (var item in list)
{
result.Add(new QueryTradeRisk()
{
TradeNumber = tradeNumberDict.ContainsKey(item.TradeId) ? tradeNumberDict[item.TradeId] : "",
ValueDate = item.ValueDate.ToString("yyyy-MM-dd"),
Pv = item.Pv,
Delta = item.Delta,
Gamma = item.Gamma,
Vega = item.Vega,
Theta = item.Theta,
Rho = item.Rho
});
}
return JsonSuccess(new { rows = result });
}
/// <summary>
/// 更新交易持仓预付金
/// </summary>
[HttpPost, Route("api/v1/updateTradePositionMargin")]
public ApiResponseModel UpdateTradePositionMargin([FromBody] eod_trade_risk_manualDto req)
{
new EodTradeRiskManualService(CurUser).SaveEodTradePositionMarginAPI(req);
return JsonSuccess();
}
/// <summary>
/// 情景分析计算
/// </summary>
[HttpPost, Route("api/v1/risk/scenario_calc")]
public ApiResponseModel ScenarioCalc([FromBody] ScenarioCalcApiRequest req)
{
if (req is null)
{
return JsonError("缺少必要的请求参数");
}
var result = new ScenarioCalcService(CurUser).Calculate(req);
return JsonSuccess(result);
}
}
}
@@ -0,0 +1,107 @@
using YLErp.Abstract.DataProviders;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.UnderlyingModule;
using YLErp.Modules.UnderlyingModule.ApiModudle;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 标的资产API
/// </summary>
[ManagerAuth]
public class UnderlyingController : BaseApiController
{
[HttpPost, Route("api/v1/getUnderlyingList")]
public IEnumerable<ApiUnderlyingInfo> GetUnderlyingList()
{
return new ApiDataQueryService(CurUser).GetUnderlyingList();
}
[HttpPost, Route("api/v1/getExchangeOptionList")]
public IEnumerable<ApiUnderlyingInfo> GetExchangeOptionList()
{
return new ApiDataQueryService(CurUser).GetExchangeOptionList();
}
/// <summary>
/// 实时价格查询(获取标的资产实时价格)
/// </summary>
[HttpPost]
[Route("api/v1/underlying/prices")]
public ApiResponseModel UnderlyingPrices([FromBody] UnderlyingPriceRequest req)
{
var dataSource = DataCacheProvider.GetUnderlyingDataSource();
if (req?.UnderlyingCodes is null || !req.UnderlyingCodes.Any())
{
var list = dataSource.AsQueryable().Select(n => new
{
Code = n.UnderlyingCode,
Price = n.IsBasket() ? BasketUnderlyingHelper.GetBasketPrice(n, dataSource as IBasketPriceProvider, true).Price : n.Price
});
return JsonSuccess(list);
}
else
{
var list = req.UnderlyingCodes.ToHashSet(StringComparer.OrdinalIgnoreCase).Select(n => new
{
Code = n,
Price = dataSource.TryGetPrice(n, out var p) ? (double?)p : null
});
return JsonSuccess(list);
}
}
/// <summary>
/// 日终价格查询(获取标的资产历史日期结算价)
/// </summary>
[HttpPost]
[Route("api/v1/underlying/eod_prices")]
public ApiResponseModel UnderlyingEodPrices([FromBody] UnderlyingEodPriceRequest req)
{
if (req is null || req.ValueDate == default)
{
return JsonError("缺少必要的请求参数");
}
var eodPriceProvider = new EodPriceProvider(req.ValueDate).Initialize(req.UnderlyingCodes);
var list = eodPriceProvider.GetEodPriceList();
if (list != null && list.Count() > 0)
{
list = list.Where(p => p != null);
}
if (list != null && list.Count() > 0)
{
return JsonSuccess(list.Select(n => new
{
Code = n.UnderlyingCode,
High = n.HighPrice,
Low = n.LowPrice,
Close = n.ClosePrice,
Settle = n.SettlePrice,
Refer = n.ReferencePrice
}));
}
else
{
return JsonSuccess(null);
}
}
/// <summary>
/// 分红率查询(获取标的资产分红率)
/// </summary>
[HttpPost]
[Route("api/v1/underlying/dividend_rate")]
public ApiResponseModel UnderlyingDividendRate([FromBody] SingleUnderlyingRequest req)
{
var list = new UnderlyingDividendService(CurUser).GetHisList(req?.UnderlyingCode);
var list2 = list.Select(n => new { ValueDate = n.ValueDate.OtcFormatDate(), n.DividendRate });
return JsonSuccess(list2);
}
}
}
@@ -0,0 +1,148 @@
using Microsoft.AspNetCore.Authorization;
using Qdp.Pricing.Base.Enums;
using YLErp.QdpModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 工具类API
/// </summary>
[ManagerAuth]
public class UtilsController : BaseApiController
{
/// <summary>
/// 观察日计算
/// </summary>
[HttpPost]
[Route("api/v1/utils/observationDates")]
public ApiResponseModel GetObservationDateList([FromBody] ObservationDatesRequest req)
{
if (req is null)
{
return JsonError("缺少必要的请求参数");
}
//观察日间隔
if (string.IsNullOrEmpty(req.Interval))
{
req.Interval = "1D";
}
//节假日调整,可选值:None 不调整、Previous 向前调整、Following 向后调整,默认:向后调整
var bdc = BusinessDayConvention.Following;
if (!string.IsNullOrWhiteSpace(req.HolidayAdjustment))
{
switch (req.HolidayAdjustment)
{
case "Following":
case "向后调整":
bdc = BusinessDayConvention.Following; break;
case "None":
case "不调整":
bdc = BusinessDayConvention.None; break;
case "Previous":
case "向前调整":
bdc = BusinessDayConvention.Previous; break;
default: return JsonError("请求参数'HolidayAdjustment'的值不符合要求");
}
}
//对齐规则,可选值:EndDate 向到期日对齐、StartDate 向开始日对齐,默认:向到期日对齐
var alignEnd = true;
if (!string.IsNullOrEmpty(req.Alignment))
{
switch (req.Alignment)
{
case "EndDate":
case "向到期日对齐":
alignEnd = true; break;
case "StartDate":
case "向开始日对齐":
alignEnd = false; break;
default: return JsonError("请求参数'Alignment'的值不符合要求");
}
}
if (!string.IsNullOrEmpty(req.CalcMode))
{
switch (req.CalcMode)
{
case "00":
case "01":
case "10":
case "11": break;
default: return JsonError("请求参数'CalcMode'的值不符合要求");
}
}
else
{
req.CalcMode = "01";
}
var list = QdpObservationHelper.GetDatesWithFixedTerm(req.StartDate, req.EndDate, req.Interval, bdc, alignEnd, req.CalcMode);
return JsonSuccess(list.Select(n => n.ToString("yyyy-MM-dd")));
}
/// <summary>
/// 用于自定义交易外接API测试
/// </summary>
[HttpPost, AllowAnonymous]
[Route("api/v1/structure/calculate")]
public object CustomTradeCalculate([FromBody] Dictionary<string, object> req)
{
if (req is null)
{
return JsonError("缺少必要的请求参数");
}
req.TryGetValue("CalcId", out var CalcId);
req.TryGetValue("Volatility", out var VolatilityObj);
double.TryParse(VolatilityObj?.ToString(), out var Volatility);
var result = new TempValueResult
{
RequestData = req,
CalcId = CalcId?.ToString(),
Pv = 100,
Vega = 100,
Delta = 100,
Gamma = 100,
Rho = 100,
Theta = 100,
Margin = 100,
Volatility = Volatility
};
return result;
}
class TempValueResult
{
public int errcode { get; set; }
public string errmsg { get; set; }
public string CalcId { get; set; }
public double Pv { get; set; }
public double Vega { get; set; }
public double Delta { get; set; }
public double Gamma { get; set; }
public double Rho { get; set; }
public double Theta { get; set; }
public double Margin { get; set; }
public double Volatility { get; set; }
public Dictionary<string, object> RequestData { get; set; }
}
}
}
@@ -0,0 +1,89 @@
using YLErp.Modules.VolatilityModule;
using YLErp.Modules.VolatilityModule.ApiModule;
namespace YLWebAPI.ApiModule.ManagerApi
{
/// <summary>
/// 关于波动率曲面相关API处理
/// </summary>
[ManagerAuth]
public class VolSurfaceController : BaseApiController
{
/// <summary>
/// 读取波动率曲面
/// </summary>
[HttpPost, Route("api/v1/getUnderlyingVolSurface")]
public ApiResponseModel GetUnderlyingVolSurface([FromBody] BatchVolatilityRequest2 reqModel)
{
if (reqModel is null)
{
return JsonError("处理请求失败:参数为null");
}
reqModel.UserGroup = CurUser.UserGroup;
var uVols = new UnderlyingVolService(CurUser).GetUnderlyingVolSurfaces(reqModel);
return JsonSuccess(uVols);
}
/// <summary>
/// 修改波动率曲面
/// </summary>
[HttpPost, Route("api/v1/saveVolatility")]
public ApiResponseModel SaveVolatility([FromBody] volatility reqModel)
{
if (reqModel is null)
{
return JsonError("参数错误");
}
reqModel.UserGroup = CurUser.UserGroup;
var vol = new UnderlyingVolService(CurUser).SaveVolatility(reqModel);
return JsonSuccess(vol);
}
/// <summary>
/// 读取波动率曲面
/// </summary>
[HttpPost, Route("api/v2/getUnderlyingVolSurface")]
public ApiResponseModel GetUnderlyingVolSurfaceV2([FromBody] UnderlyingVolQueryApiRequestV2 request)
{
if (request is null)
{
return JsonError("处理请求失败:参数为null");
}
var uVols = new UnderlyingVolServiceV2(CurUser).GetVolSurfaces(request);
return JsonSuccess(uVols);
}
/// <summary>
/// 修改波动率曲面
/// </summary>
[HttpPost, Route("api/v2/saveVolatility")]
public ApiResponseModel SaveVolatilityV2([FromBody] UnderlyingVolSaveApiRequestV2 request)
{
if (request is null)
{
return JsonError("参数错误");
}
request.UserGroup = CurUser.UserGroup;
var vol = new UnderlyingVolServiceV2(CurUser).SaveVolSurface(request);
return JsonSuccess(vol);
}
/// <summary>
/// 从波动率曲面上根据参数获取一个点的值
/// </summary>
[HttpPost, Route("api/v1/getVol")]
public ApiResponseModel AjaxGetVol(SingleVolReq req)
{
var vol = SingleVolService.GetSingleVol(req, CurUser.UserId);
return JsonSuccess(new { vol });
}
}
}
@@ -0,0 +1,23 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 工作日天数计算
/// </summary>
public class GetWorkingDayCountRequest
{
/// <summary>
/// 日历名称
/// </summary>
public string CalendarName { get; set; }
/// <summary>
/// 开始日
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// 结束日
/// </summary>
public DateTime EndDate { get; set; }
}
}
@@ -0,0 +1,18 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
///
/// </summary>
public class ClientApiTokenResponseModel : ApiTokenResponseModel
{
/// <summary>
///
/// </summary>
public int clientId { get; set; }
/// <summary>
///
/// </summary>
public string guid { get; set; }
}
}
@@ -0,0 +1,50 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 结算报告:获取客户财务状况
/// </summary>
public class ClientLatestBalanceRequestModel
{
/// <summary>
/// 开始日期
/// </summary>
public DateTime? ValueDateFrom { get; set; }
/// <summary>
/// 结束日期
/// </summary>
public DateTime? ValueDateTo { get; set; }
/// <summary>
/// 是否外部预付金
/// </summary>
public bool IsGetOuterMarginGap { get; set; }
/// <summary>
/// 客户ID
/// </summary>
public int ClientId { get; set; }
/// <summary>
/// 客户名称,有值的情况下根据这个推算clientId
/// </summary>
public string ClientName { get; set; }
/// <summary>
/// 客户编号,有值的情况下根据这个推算clientId
/// 优先于ClientName
/// </summary>
public string ClientNumber { get; set; }
}
/// <summary>
/// 带分页查询的客户数据请求参数
/// </summary>
public class ClientLatestBalancePagedRequestModel: ClientLatestBalanceRequestModel
{
public int? Page { get; set; }
public int? PageRowCount { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 结算报告:查看交易详情
/// </summary>
public class ClientTradeDetailRequestModel
{
public string EncryptedId { get; set; }
}
}
@@ -0,0 +1,16 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 结算报告:获取客户持仓信息
/// </summary>
public class ClientTradePositionRequestModel
{
public DateTime ValueDate { get; set; }
public int ClientId { get; set; }
public int Page { get; set; }
public int PageRowCount { get; set; } = 10;
}
}
@@ -0,0 +1,99 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 组合交易主交易修改请求
/// </summary>
public class GroupTradeMainEditRequest
{
/// <summary>
/// 交易编号(不参与修改)
/// </summary>
public string TradeNumber { get; set; }
/// <summary>
/// 到期日期
/// </summary>
public DateTime ExerciseDate { get; set; }
/// <summary>
/// 买卖方向
/// </summary>
public string BuySell { get; set; }
/// <summary>
/// 相对行权价
/// </summary>
public string IsMoneynessOption { get; set; }
/// <summary>
/// 名义本金
/// StockEqvNotional和TradeAmount必须有一个有值,优先使用StockEqvNotional
/// </summary>
public double? StockEqvNotional { get; set; }
/// <summary>
/// 成交数量
/// </summary>
public double? TradeAmount { get; set; }
/// <summary>
/// 结构类型
/// </summary>
public string StructureType { get; set; }
/// <summary>
/// 交易备注
/// </summary>
public string Comments { get; set; }
/// <summary>
/// 簿记账户
/// </summary>
public string AssetBookName { get; set; }
/// <summary>
/// 交易员
/// </summary>
public string TraderName { get; set; }
/// <summary>
/// 扩展信息
/// </summary>
public string ExtendInfo { get; set; }
/// <summary>
/// 气囊结构--看涨看跌
/// </summary>
public string OptionType { get; set; }
/// <summary>
/// 气囊结构--未敲入参与率
/// </summary>
public double? NotKIParticipationRate { get; set; }
/// <summary>
/// 气囊结构--敲入价格
/// </summary>
public double? BarrierLow { get; set; }
/// <summary>
/// 气囊结构--低执行价格
/// </summary>
public double? Strike { get; set; }
/// <summary>
/// 气囊结构--高执行价格
/// </summary>
public double? HighStrike { get; set; }
/// <summary>
/// 气囊结构--是否收益封顶
/// </summary>
public bool HasPayoffLimit { get; set; }
/// <summary>
/// 期初标的价格
/// </summary>
public double? SpotPrice { get; set; }
}
}
@@ -0,0 +1,36 @@
namespace YLWebAPI.ApiModule.Models
{
public class QueryTradeRiskReq
{
/// <summary>
/// 交易编号
/// </summary>
public string[] TradeNumbers { get; set; }
/// <summary>
/// 估值日期
/// </summary>
public DateTime ValueDate { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public int DataType { get; set; }
}
public class QueryTradeRisk
{
/// <summary>
/// 交易编号
/// </summary>
public string TradeNumber { get; set; }
/// <summary>
/// 估值日期
/// </summary>
public string ValueDate { get; set; }
public double Pv { get; set; }
public double Delta { get; set; }
public double Gamma { get; set; }
public double Vega { get; set; }
public double Theta { get; set; }
public double Rho { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 实时结算数据请求
/// </summary>
public class RealTimeSettlementInfoAllRequest
{
public DateTime? StartDate { get; set; }
}
}
@@ -0,0 +1,29 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 结算已准备好的信息
/// </summary>
public class SettlementReadyInfo
{
/// <summary>
/// 获取收盘成功的最后一个结算日
/// </summary>
public string LastSettlementDate { get; set; }
/// <summary>
/// 收盘核对状态
/// </summary>
public bool CheckStatus { get; set; }
}
/// <summary>
/// 请求查询结算已准备好的信息
/// </summary>
public class SettlementReadyInfoReuqest
{
/// <summary>
/// 清算日期
/// </summary>
public DateTime? ValueDate { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 当日场内交易汇总请求
/// </summary>
public class TodayExchangePositionsReqModel
{
public DateTime? ValueDate { get; set; }
}
}
@@ -0,0 +1,23 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 保存场外期权交易有历史数据记录的请求
/// </summary>
public class TradeHisDataUpdateRequest
{
/// <summary>
/// 交易编号
/// </summary>
public string TradeNumber { get; set; }
///// <summary>
///// 赋值日期
///// </summary>
//public DateTime? ValueDate { get; set; }
/// <summary>
/// 波动率值
/// </summary>
public double Value { get; set; }
}
}
@@ -0,0 +1,13 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 删除交易请求
/// </summary>
public class TradeInvalidRequest
{
/// <summary>
/// 交易编号
/// </summary>
public string TradeNumber { get; set; }
}
}
@@ -0,0 +1,28 @@
namespace YLWebAPI.ApiModule.Models
{
/// <summary>
/// 单标的资产信息查询
/// </summary>
public class SingleUnderlyingRequest
{
public string UnderlyingCode { get; set; }
}
/// <summary>
/// 实时价格查询(获取标的资产实时价格)
/// </summary>
public class UnderlyingPriceRequest
{
public IEnumerable<string> UnderlyingCodes { get; set; }
}
/// <summary>
/// 日终价格查询(获取标的资产历史日期结算价)
/// </summary>
public class UnderlyingEodPriceRequest
{
public DateTime ValueDate { get; set; }
public IEnumerable<string> UnderlyingCodes { get; set; }
}
}
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>c7e0f300-16d5-438b-97a2-79dc2311d1d5</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>YLWebAPI.ApiModule</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)BaseApiController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\EnumConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\IsoDateTimeConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedBooleanConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedByteConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedDecimalConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedDoubleConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedFloatConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedInt16Converter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedInt32Converter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\QuotedInt64Converter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\SerializerOptionsConfigration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filter\ActionLogFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AspNetCustom\LoadUserMiddleware.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\RealTimeController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ClientCashController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\FangDunController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\GTJASwapTradeController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\InnerTestController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\OtcOptionTradeController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\EodQueryController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\CalendarController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradeContractDocController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\UtilsController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ProcessLogController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\SettlementController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\SystemController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradePricingController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradePublicInfoQueryController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradeQueryController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AspNetCustom\JsonBodyBinderProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ClientApiTokenResponseModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\GroupTradeMainEditRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\QueryTradeRiskReq.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\RealTimeSettlementInfoAllRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\SettlementReadyInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TradeInvalidRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\CalendarRequests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\UnderlyingRequests.cs" />
<None Include="$(MSBuildThisFileDirectory)readme.txt" />
<Compile Include="$(MSBuildThisFileDirectory)ApiHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ClientApi\ClientAPiController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ClientApi\ClientAuthAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ClientApi\ClientTokenController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ClientApi\SystemDataController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ClientDataController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ExchangeTradeController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ManagerAuthAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\ManagerTokenController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\OrderApiController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\SysInfoController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradeDealController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\TradeRiskCalcController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\UnderlyingController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ManagerApi\VolSurfaceController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ClientLatestBalanceRequestModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ClientTradeDetailRequestModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ClientTradePositionRequestModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TradeHisDataUpdateRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TodayExchangePositionsReqModel.cs" />
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>c7e0f300-16d5-438b-97a2-79dc2311d1d5</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="SharedWebApi.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>
+4
View File
@@ -0,0 +1,4 @@
ClientApi -- 用于存放客户端请求访问的API
ManagerApi -- 用于存放管理端请求访问的API
@@ -0,0 +1,17 @@
using YLErp.Models;
namespace YLErp.Abstract.DataProviders
{
/// <summary>
/// 客户数据提供
/// </summary>
public interface IClientDataProvider
{
/// <summary>
/// 获取客户主要信息
/// </summary>
/// <param name="clientId">客户ID</param>
/// <param name="throwIfNotFound">如果未找到是否抛出异常</param>
ClientMainInfo GetClientInfo(int clientId, bool throwIfNotFound = false);
}
}
@@ -0,0 +1,20 @@
namespace YLErp.Abstract.DataProviders
{
/// <summary>
/// 数据提供缓存接口
/// </summary>
public interface IDataProviderCache
{
/// <summary>
/// 设置缓存数据
/// timeoutMilliseconds:缓存过期设置(单位:毫秒)
/// </summary>
void SetCache(string key, object data, int timeoutMilliseconds);
/// <summary>
/// 获取缓存数据
/// timeoutMilliseconds:如果数据缓存生存时间大于此值则返回null
/// </summary>
object GetCache(string key, int timeoutMilliseconds);
}
}
@@ -0,0 +1,18 @@
namespace YLErp.Abstract.DataProviders
{
/// <summary>
/// 场内数据信息提供
/// </summary>
public interface IExchangeOptionDataProvider
{
/// <summary>
/// 尝试获取场内期权过期日
/// </summary>
bool TryGetMaturityDate(string optionCode, out DateTime date);
/// <summary>
/// 尝试获取行情价格
/// </summary>
bool TryGetPrice(string optionCode, out double price);
}
}
@@ -0,0 +1,76 @@
using YLErp.DBModels;
using YLErp.Models;
namespace YLErp.Abstract.DataProviders
{
/// <summary>
/// 价格提供接口
/// </summary>
public interface IPriceProvider
{
/// <summary>
/// 获取价格
/// </summary>
double GetPrice(string instrumentCode);
/// <summary>
/// 获取价格
/// </summary>
bool TryGetPrice(string instrumentCode, out double price);
}
/// <summary>
/// 日终结算价获取
/// </summary>
public interface IEodPriceProvider
{
/// <summary>
/// 获取价格
/// </summary>
double GetPrice(string instrumentCode, SettlementTypeEnum settlementType);
/// <summary>
/// 获取价格
/// </summary>
bool TryGetPrice(string instrumentCode, SettlementTypeEnum settlementType, out double price);
}
/// <summary>
/// 日终结算价获取
/// </summary>
public interface IEodPriceProviderV2
{
/// <summary>
/// 获取标的收盘价或结算价(如果标的在取值日之前已到期,则取标的到期日的收盘价或结算价)
/// </summary>
bool TryGetEodPrice(string instrumentCode, out EodPrice eodPrice);
}
/// <summary>
/// 日终价格封装接口
/// </summary>
public interface IEodPriceProviderWrap : IPriceProvider
{
SettlementTypeEnum SettlementType { get; }
}
/// <summary>
/// 篮子标的价格提供接口
/// 特别抽出这个接口也是为了防止数据库被恶意配置了篮子标的的死循环引用数据
/// </summary>
public interface IBasketPriceProvider
{
/// <summary>
/// 获取篮子的子标的价格
/// </summary>
bool TryGetSubPrice(string underlyingCode, out double price, out double settlePrice);
}
/// <summary>
/// 多个价格提供接口聚合提供价格
/// </summary>
public interface IAggregatePriceProvider : IPriceProvider
{
}
}
@@ -0,0 +1,102 @@
using YLErp.DBModels;
namespace YLErp.Abstract
{
/// <summary>
/// 交易扩展数据提供接口
/// </summary>
public interface ITradeExtendDataProvider
{
/// <summary>
/// 彩虹期权
/// </summary>
trade_rainbow_option GetTrade_Rainbow_Option(int tradeId);
/// <summary>
/// 亚式期权
/// </summary>
trade_asian_option GetTrade_Asian_Option(int tradeId);
/// <summary>
/// 障碍期权
/// </summary>
trade_barrier_option GetTrade_Barrier_Option(int tradeId);
/// <summary>
/// 二元期权
/// </summary>
trade_binary_option GetTrade_Binary_Option(int tradeId);
/// <summary>
/// 价差期权
/// </summary>
trade_spread_option GetTrade_Spread_Option(int tradeId);
/// <summary>
/// 双鲨期权
/// </summary>
trade_double_sharkfin_option GetTrade_Double_SharkFin_Option(int tradeId);
/// <summary>
/// 凤凰期权
/// </summary>
trade_autocall GetTrade_Autocall_Option(int tradeId);
/// <summary>
/// 凤凰期权--累积票息
/// </summary>
List<autocall_observation> GetTrade_HappenedObservations(int tradeId);
/// <summary>
/// 雪球期权
/// </summary>
trade_snowball GetTrade_Snowball_Option(int tradeId);
/// <summary>
/// 区间累积期权
/// </summary>
trade_rangeaccrual GetTrade_RangeAccrual(int tradeId);
/// <summary>
/// 现金流交易
/// </summary>
trade_cashflow GetTrade_CashFlow(int tradeId);
/// <summary>
/// 气囊结构
/// </summary>
trade_airbag GetTrade_Airbag(int tradeId);
/// <summary>
/// 累计期权(累购/累沽)
/// </summary>
trade_accumulator_option GetTrade_Accumulator_Option(int tradeId);
/// <summary>
/// Risky期权
/// </summary>
trade_risky_option GetTrade_Risky_Option(int tradeId);
/// <summary>
/// 收益互换
/// </summary>
trade_swap GetTrade_Swap(int tradeId);
/// <summary>
/// 收益互换 多空组合详情
/// </summary>
List<trade_swap_detail> GetTrade_Swap_Details(int tradeId);
/// <summary>
/// 收益增强结构
/// </summary>
trade_underlying_enhance GetTrade_UnderlyingEnhance(int tradeId);
/// <summary>
/// 远期
/// </summary>
/// <param name="tradeId"></param>
/// <returns></returns>
trade_forward GetTrade_Forward(int tradeId);
}
}
@@ -0,0 +1,18 @@
namespace YLErp.Abstract
{
/// <summary>
/// 交易历史数据提供接口(valuedate在实现中处理)
/// </summary>
public interface ITradeHisDataProvider
{
/// <summary>
/// 获取无风险利率
/// </summary>
double? GetNoRiskRate(int tradeId);
/// <summary>
/// 获取分红率
/// </summary>
double? GetDividendRate(int tradeId);
}
}
@@ -0,0 +1,72 @@
using YLErp.DBModels;
namespace YLErp.Abstract.DataProviders
{
/// <summary>
/// 标的数据基础提供接口
/// </summary>
public interface IUnderlyingProvider
{
/// <summary>
/// 根据标的ID获取标的
/// </summary>
underlying_manager GetUnderlying(int underlyingId);
/// <summary>
/// 根据标的代码获取标的
/// </summary>
underlying_manager GetUnderlying(string underlyingCode);
/// <summary>
/// 根据标的代码获取标的
/// </summary>
underlying_manager GetUnderlying(string underlyingCode, out double contractSize);
}
/// <summary>
/// 标的数据及关联数据提供
/// </summary>
public interface IUnderlyingDataProvider : IUnderlyingProvider
{
/// <summary>
/// 获取品种信息
/// </summary>
Variety GetVariety(int varietyId);
/// <summary>
/// 获取品种数据
/// </summary>
/// <param name="underlyingCode">标的代码</param>
Variety GetVariety(string underlyingCode);
/// <summary>
/// 获取品种信息
/// </summary>
Variety GetVariety(string underlyingCode, out double contractSize);
/// <summary>
/// 根据标的代码获取份额和数量的乘积因子(最小为1)
/// </summary>
int GetCountRatio(string underlyingCode);
/// <summary>
/// 尝试获取标的过期日(股票:2099-01-01),如果标的信息不存在返回false
/// </summary>
bool TryGetMaturityDate(string underlyingCode, out DateTime date);
/// <summary>
/// 根据标的代码获取场内期权标的
/// </summary>
ExchangeListOption GetExchange_List_Option(string ContractCode);
/// <summary>
/// 获取相关性
/// </summary>
CorrelationTable GetCorrelation(int underlyingId1, int underlyingId2);
/// <summary>
/// 获取组合标的
/// </summary>
SyntheticUnderlying GetSyntheticUnderlying(string underlyingCode);
}
}
@@ -0,0 +1,36 @@
namespace YLErp.Abstract
{
/// <summary>
/// 缓存提供接口
/// </summary>
public interface ICacheProvider
{
/// <summary>
/// 删除指定的项
/// </summary>
void Remove(string key);
/// <summary>
/// 检索指定的项
/// </summary>
object Get(string key);
/// <summary>
/// 设置缓存对象
/// </summary>
/// <param name="key">用于引用该对象的缓存密钥</param>
/// <param name="value">要插入缓存中的对象</param>
/// <param name="absoluteExpiration">从该处插入的对象过期并从缓存中删除的时间</param>
void Set(string key, object value, DateTimeOffset absoluteExpiration);
/// <summary>
/// 设置缓存对象
/// </summary>
/// <param name="key">用于引用该对象的缓存密钥</param>
/// <param name="value">要插入缓存中的对象</param>
/// <param name="absoluteExpirationRelativeToNow">
/// 对象的到期时间和上次访问所插入的对象的时间之间的间隔。 如果此值为 20 分钟的等效项,该对象会过期,可从缓存中删除上次访问后的 20 分钟。
/// </param>
void Set(string key, object value, TimeSpan absoluteExpirationRelativeToNow);
}
}
@@ -0,0 +1,49 @@
using YLErp.DBModels;
using YLErp.DBModels.Abstract;
namespace YLErp.Abstract
{
public interface IContractBLL
{
/// <summary>
/// 删除确认书
/// </summary>
void DeleteConfirmBook(int tradeId, string contractType = "交易确认书");
/// <summary>
/// 获取所有文档
/// </summary>
IEnumerable<ITradeContractDocument> GetAllContractDocs(int tradeId);
/// <summary>
/// 获取确认书
/// </summary>
ITradeContractDocument GetContractDoc(int tradeId, string contractType, int tradeCashId = 0, DateTime? valueDate = null);
/// <summary>
/// 获取确认书
/// </summary>
IEnumerable<ITradeContractDocument> GetContractDoc(int clientId, string contractType, DateTime valueDate);
/// <summary>
/// 获取确认书
/// </summary>
Dictionary<long, ITradeContractDocument> GetContractDocDic(List<long> swapFlowEventIss, string contractType, DateTime valueDate);
/// <summary>
///
/// </summary>
string GetContractNo(string ruleId);
/// <summary>
///
/// </summary>
int GetRuleIndex(string ruleId, int beginIndex = 1);
/// <summary>
/// 检查相同合约书号是否对应的相同客户相同交易方向的确认书编号(买卖方向)
/// </summary>
bool HasRepeatContractCodeForDiffClient(string ContractCode, string clientName, string BuySell, out string TradeNumber);
trade_contract_r GetTrade_Contract_R(int tradeId);
}
}
@@ -0,0 +1,60 @@
using System.Linq.Expressions;
namespace YLErp.Abstract
{
/// <summary>
/// 数据源接口
/// </summary>
public interface IDataSource
{
/// <summary>
/// 数据个数
/// </summary>
int Count { get; }
/// <summary>
/// 重置数据源
/// </summary>
void ResetDataSource();
}
/// <summary>
/// 数据源接口
/// </summary>
public interface IDataSource<TData> : IDataSource
{
TData GetData(int keyId);
/// <summary>
/// 获取数据
/// </summary>
IQueryable<TData> AsQueryable(Expression<Func<TData, bool>> predicate = null);
}
/// <summary>
/// 数据源接口
/// </summary>
public interface IDataSourceV2<TData> : IDataSource<TData>
{
bool TryGetData(int keyId, out TData data);
}
/// <summary>
/// 数据源接口
/// </summary>
public interface IDataSourceKey2<TData> : IDataSource<TData>
{
TData GetData(string keyStr);
}
/// <summary>
/// 数据源事件接口
/// </summary>
public interface IDataSourceEvent
{
/// <summary>
/// 数据源被更新后的事件
/// </summary>
event EventHandler DataSourceUpdated;
}
}
@@ -0,0 +1,23 @@
namespace YLErp.Abstract
{
/// <summary>
/// 错误处理接口(基本用于循环处理中发生错误的场景)
/// </summary>
public interface IErrorHandler
{
/// <summary>
///
/// </summary>
bool HasError { get; }
/// <summary>
///
/// </summary>
void AddError(string error);
/// <summary>
///
/// </summary>
IEnumerable<string> GetErrors();
}
}
@@ -0,0 +1,7 @@
namespace YLErp.Abstract
{
public interface IJsonSerializable
{
string ToJson();
}
}
@@ -0,0 +1,10 @@
namespace YLErp.Abstract
{
/// <summary>
/// 带有OptUser的对象
/// </summary>
public interface IOptUserBase
{
OptUserInfo OptUser { get; set; }
}
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using YLErp.DBModels;
using YLErp.DBModels.Abstract;
namespace YLErp.Abstract
{
public interface ISealTaskHandler
{
void AddQueue(ITradeContractDocument confirmDoc,int tradeId);
void AddQueue(ITradeContractDocument confirmDoc, List<int> tradeIds);
bool IsQueueExistsCode(string code);
void RemoveQueue(int tradeId);
void SaveTradeAuditLog(int tradeId, string changes, string optType, DateTime optDate);
void UpdateTradeContractDocument(ITradeContractDocument document, int sealResult, string errMsg);
/// <summary>
///
/// </summary>
/// <param name="document"></param>
/// <param name="keyword"></param>
/// <returns>
/// <para>1:用印是否成功</para>
/// <para>2:用印接口返回信息</para>
/// <para>3:用印文件base64字符串</para>
/// </returns>
Task<(bool, string, string)> UploadApiAsync(ITradeContractDocument doc, string keyword);
}
}
@@ -0,0 +1,20 @@
namespace YLErp.Abstract
{
/// <summary>
/// 构造一个波动率曲面方法参数
/// </summary>
public interface ITradeVolLinearParam
{
double GetTradeOpenVol();
double GetTradeCloseVol();
DateTime GetStartDate();
DateTime GetMaturityDate();
int GetNumOfSmoothingDays();
bool IsIncludeStartDate();
}
}
@@ -0,0 +1,16 @@
using YLErp.Models;
namespace YLErp.Abstract
{
/// <summary>
/// 波动率数据接口
/// </summary>
public interface IVolatility
{
string VolSurfaceMode { get; }
string InterpolationMethod { get; }
List<SingleVol> VolTable { get; }
}
}
@@ -0,0 +1,147 @@
using System.Data;
using System.Globalization;
using System.Text.RegularExpressions;
namespace YLErp.Commons
{
/// <summary>
/// 用于读取解析DataRow数据
/// </summary>
public class DataRowReader
{
DataRow _row;
readonly Dictionary<string, int> _colMap;
public DataRowReader(DataTable table, int headRowIndex = -1)
{
var colCount = table.Columns.Count;
_colMap = new Dictionary<string, int>(colCount, StringComparer.OrdinalIgnoreCase);
for (var index = 0; index < colCount; index++)
{
if (headRowIndex >= 0)
{
var row = table.Rows[headRowIndex];
var col = row[index]?.ToString()?.Trim();
if (string.IsNullOrEmpty(col)) continue;
_colMap[col.Replace("%", "")] = index;
}
else
{
var columns = table.Columns;
var colName = columns[index].ColumnName;
colName = colName.Replace("%", "").Trim();
_colMap[colName] = index;
}
}
}
/// <summary>
/// 设置datarow
/// </summary>
public void SetDataRow(DataRow row)
{
_row = row;
}
public string GetString(string fieldName, bool required)
{
var str = _colMap.TryGetValue(fieldName, out var colIndex) ? _row[colIndex]?.ToString()?.Trim() : null;
if (required && string.IsNullOrEmpty(str))
{
throw new ServiceException($"{fieldName} 必须填写");
}
return str;
}
public double? GetDouble(string fieldName, bool required)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
return double.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误");
}
public int? GetInt(string fieldName, bool required)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
return int.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误");
}
/// <summary>
/// 获取百分比格式的数值
/// </summary>
public double? GetPercent(string fieldName, bool required)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
var isPercent = str.EndsWith("%");
if (isPercent)
{
str = str.TrimEnd('%');
}
return double.TryParse(str, out var num) ? (isPercent ? num / 100 : num) : throw new ServiceException($"{fieldName} 填写错误");
}
/// <summary>
/// 获取百分比格式的数值
/// </summary>
public decimal? GetDecimalPercent(string fieldName, bool required)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
var isPercent = str.EndsWith("%");
if (isPercent)
{
str = str.TrimEnd('%');
}
return decimal.TryParse(str, out var num) ? (isPercent ? num / 100 : num) : throw new ServiceException($"{fieldName} 填写错误");
}
/// <summary>
/// 获取日期(不包括时间)
/// </summary>
public DateTime? GetDate(string fieldName, bool required)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
if (str.Length == 8 && Regex.IsMatch(str, @"^\d+$"))
{
return DateTime.TryParseExact(str, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt2) ? dt2 : throw new ServiceException($"{fieldName} 填写错误");
}
return DateTime.TryParse(str, out var dt) ? dt.Date : throw new ServiceException($"{fieldName} 填写错误");
}
}
}
@@ -0,0 +1,164 @@
using System.Collections;
namespace YLErp.Commons
{
/// <summary>
///
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class ExpandoDictionary<TKey, TValue> : IDictionary, IDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> _dic;
public ExpandoDictionary(IDictionary<TKey, TValue> dic)
{
_dic = dic ?? throw new ArgumentNullException(nameof(dic));
}
object IDictionary.this[object key]
{
get
{
return ((IDictionary)_dic).Contains(key) ? ((IDictionary)_dic)[(TKey)key] : default;
}
set => ((IDictionary)_dic)[(TKey)key] = (TValue)value;
}
public TValue this[TKey key]
{
get => _dic.TryGetValue(key, out var value) ? value : default;
set => _dic[key] = value;
}
ICollection IDictionary.Keys => _dic.Keys.ToArray();
ICollection IDictionary.Values => _dic.Values.ToArray();
public bool IsReadOnly => _dic.IsReadOnly;
bool IDictionary.IsFixedSize => ((IDictionary)_dic).IsFixedSize;
public int Count => _dic.Count;
object ICollection.SyncRoot => ((IDictionary)_dic).SyncRoot;
bool ICollection.IsSynchronized => ((IDictionary)_dic).IsSynchronized;
ICollection<TKey> IDictionary<TKey, TValue>.Keys => _dic.Keys;
ICollection<TValue> IDictionary<TKey, TValue>.Values => _dic.Values;
void IDictionary.Add(object key, object value)
{
((IDictionary)_dic).Add(key, value);
}
public void Add(TKey key, TValue value)
{
_dic.Add(key, value);
}
public void Add(KeyValuePair<TKey, TValue> item)
{
_dic.Add(item);
}
public void Clear()
{
_dic.Clear();
}
bool IDictionary.Contains(object key)
{
return ((IDictionary)_dic).Contains(key);
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dic.Contains(item);
}
public bool ContainsKey(TKey key)
{
return _dic.ContainsKey(key);
}
void ICollection.CopyTo(Array array, int index)
{
((IDictionary)_dic).CopyTo(array, index);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dic.CopyTo(array, arrayIndex);
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return ((IDictionary)_dic).GetEnumerator();
}
void IDictionary.Remove(object key)
{
((IDictionary)_dic).Remove(key);
}
public bool Remove(TKey key)
{
return _dic.Remove(key);
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return _dic.Remove(item);
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dic.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dic.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return _dic.GetEnumerator();
}
public static implicit operator ExpandoDictionary<TKey, TValue>(Dictionary<TKey, TValue> dic)
{
return new ExpandoDictionary<TKey, TValue>(dic);
}
}
/// <summary>
///
/// </summary>
public class ExpandoDictionary
{
public delegate string FormatPropValue(string propName, object propValue);
public static ExpandoDictionary<string, string> ConvertObject(object obj, FormatPropValue format)
{
if (obj is null)
{
return null;
}
var props = TypeDescriptor.GetProperties(obj);
var dic = new Dictionary<string, string>(props.Count);
foreach (PropertyDescriptor p in props)
{
var val = p.GetValue(obj);
dic[p.Name] = val == null ? string.Empty : format != null ? format(p.Name, val) : val.ToString();
}
return dic;
}
}
}
@@ -0,0 +1,693 @@
using YLErp.Commons;
using static YLErp.Models.OtcFormatModel;
namespace System
{
/// <summary>
/// OtcFormat扩展
/// </summary>
public static class OtcFormatExtensions
{
static TradingFormat tradingFormat => OtcFormatHelper.FormatModel.trading;
public static string OtcFormat(this double? d, OtcFormatFlag fmt)
{
if (d == -0) d = 0;
return d.HasValue ? OtcFormat(d.Value, fmt) : string.Empty;
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static string OtcFormat(this double d, OtcFormatFlag fmt)
{
switch (fmt)
{
case OtcFormatFlag.umprice:
return tradingFormat.umprice.Format(d);
case OtcFormatFlag.umpriceP:
return tradingFormat.umpriceP.Format(d);
case OtcFormatFlag.umpricePR:
return tradingFormat.umpricePR.Format(d);
case OtcFormatFlag.tradePrice:
return d.ToString("0.00");
case OtcFormatFlag.tradeSinglePrice:
return tradingFormat.tradeSinglePrice.Format(d);
case OtcFormatFlag.StockEqvNotional:
return tradingFormat.StockEqvNotional.Format(d);
case OtcFormatFlag.premiumRate:
return tradingFormat.premiumRate.Format(d);
case OtcFormatFlag.premiumRateP:
return tradingFormat.premiumRateP.Format(d);
case OtcFormatFlag.notional:
return tradingFormat.notional.Format(d);
case OtcFormatFlag.notionalP:
return tradingFormat.notionalP.Format(d);
case OtcFormatFlag.greek:
return tradingFormat.greek.Format(d);
case OtcFormatFlag.percent:
return d.ToString("0.00%");
case OtcFormatFlag.percent4:
return d.ToString("0.0000%");
case OtcFormatFlag.volatility:
return tradingFormat.volatility.Format(d);
case OtcFormatFlag.volatilityP:
return tradingFormat.volatilityP.Format(d);
case OtcFormatFlag.marginRate:
return tradingFormat.marginRate.Format(d);
case OtcFormatFlag.marginRateP:
return tradingFormat.marginRateP.Format(d);
default: return d.ToString("0.00");
}
}
public static string OtcFormat(this decimal? d, OtcFormatFlag fmt)
{
if (d == -0) d = 0;
return d.HasValue ? OtcFormat(d.Value, fmt) : string.Empty;
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static string OtcFormat(this decimal d, OtcFormatFlag fmt)
{
switch (fmt)
{
case OtcFormatFlag.umprice:
return tradingFormat.umprice.Format(d);
case OtcFormatFlag.umpriceP:
return tradingFormat.umpriceP.Format(d);
case OtcFormatFlag.umpricePR:
return tradingFormat.umpricePR.Format(d);
case OtcFormatFlag.tradePrice:
return d.ToString("0.00");
case OtcFormatFlag.tradeSinglePrice:
return tradingFormat.tradeSinglePrice.Format(d);
case OtcFormatFlag.StockEqvNotional:
return tradingFormat.StockEqvNotional.Format(d);
case OtcFormatFlag.premiumRate:
return tradingFormat.premiumRate.Format(d);
case OtcFormatFlag.premiumRateP:
return tradingFormat.premiumRateP.Format(d);
case OtcFormatFlag.notional:
return tradingFormat.notional.Format(d);
case OtcFormatFlag.notionalP:
return tradingFormat.notionalP.Format(d);
case OtcFormatFlag.greek:
return tradingFormat.greek.Format(d);
case OtcFormatFlag.percent:
return d.ToString("0.00%");
case OtcFormatFlag.percent4:
return d.ToString("0.0000%");
case OtcFormatFlag.volatility:
return tradingFormat.volatility.Format(d);
case OtcFormatFlag.volatilityP:
return tradingFormat.volatilityP.Format(d);
case OtcFormatFlag.marginRate:
return tradingFormat.marginRate.Format(d);
case OtcFormatFlag.marginRateP:
return tradingFormat.marginRateP.Format(d);
default: return d.ToString("0.00");
}
}
/// <summary>
/// 数字格式化
/// </summary>
public static string OtcFormatFlex(this double? d, int minDecimals, int maxDecimals = 6
, bool percent = false, bool grouping = false, bool rounded = true)
{
if (d.HasValue)
{
var d2 = d.Value;
if (double.IsNaN(d2) || double.IsInfinity(d2))
{
return string.Empty;
}
if (!rounded)
{
d2 = NumberHelper.Truncate(d2, minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent);
}
return d2.ToString(NumberHelper.GetFormat(minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent, grouping: grouping));
}
return string.Empty;
}
/// <summary>
/// 数字格式化
/// </summary>
public static string OtcFormatFlex(this double d, int minDecimals, int maxDecimals = 6
, bool percent = false, bool grouping = false, bool rounded = true)
{
if (double.IsNaN(d) || double.IsInfinity(d))
{
return string.Empty;
}
if (!rounded)
{
d = NumberHelper.Truncate(d, minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent);
}
return d.ToString(NumberHelper.GetFormat(minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent, grouping: grouping));
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static double OtcFormatValue(this double d, OtcFormatFlag fmt)
{
switch (fmt)
{
case OtcFormatFlag.umprice:
return tradingFormat.umprice.FormatValue(d);
case OtcFormatFlag.umpriceP:
return tradingFormat.umpriceP.FormatValue(d);
case OtcFormatFlag.umpricePR:
return tradingFormat.umpricePR.FormatValue(d);
case OtcFormatFlag.tradePrice:
return tradingFormat.tradePrice.FormatValue(d);
case OtcFormatFlag.tradeSinglePrice:
return tradingFormat.tradeSinglePrice.FormatValue(d);
case OtcFormatFlag.StockEqvNotional:
return tradingFormat.StockEqvNotional.FormatValue(d);
case OtcFormatFlag.premiumRate:
return tradingFormat.premiumRate.FormatValue(d);
case OtcFormatFlag.premiumRateP:
return tradingFormat.premiumRateP.FormatValue(d);
case OtcFormatFlag.notional:
return tradingFormat.notional.FormatValue(d);
case OtcFormatFlag.notionalP:
return tradingFormat.notionalP.FormatValue(d);
case OtcFormatFlag.greek:
return tradingFormat.greek.FormatValue(d);
case OtcFormatFlag.percent:
return OtcFormatHelper.FormatValue(d * 100, 2);
case OtcFormatFlag.percent4:
return OtcFormatHelper.FormatValue(d * 100, 4);
case OtcFormatFlag.volatility:
return tradingFormat.volatility.FormatValue(d);
case OtcFormatFlag.volatilityP:
return tradingFormat.volatilityP.FormatValue(d);
case OtcFormatFlag.marginRate:
return tradingFormat.marginRate.FormatValue(d);
case OtcFormatFlag.marginRateP:
return tradingFormat.marginRateP.FormatValue(d);
default: return OtcFormatHelper.FormatValue(d, 2);
}
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static double? OtcFormatValue(this double? d, OtcFormatFlag fmt)
{
return d.HasValue ? OtcFormatValue(d.Value, fmt) : d;
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static double OtcFormatValue(this double d, int precision, bool rounded = true)
{
return OtcFormatHelper.FormatValue(d, precision, rounded);
}
/// <summary>
/// 系统标的价格格式化
/// </summary>
public static string OtcFormatUmPrice(this double d, bool isMoneyness = false)
{
return isMoneyness ? tradingFormat.umpriceP.Format(d) : tradingFormat.umprice.Format(d);
}
/// <summary>
/// 系统标的价格格式化
/// </summary>
public static string OtcFormatUmPrice(this decimal d, bool isMoneyness = false)
{
return isMoneyness ? tradingFormat.umpriceP.Format(Convert.ToDouble(d)) : tradingFormat.umprice.Format(Convert.ToDouble(d));
}
/// <summary>
/// 系统标的价格格式化
/// </summary>
public static string OtcFormatUmPrice(this double? d, bool isMoneyness = false)
{
if (d.HasValue)
{
return isMoneyness ? tradingFormat.umpriceP.Format(d.Value) : tradingFormat.umprice.Format(d.Value);
}
return string.Empty;
}
public static string OtcFormatUmPrice(this decimal? d, bool isMoneyness = false)
{
if (d.HasValue)
{
return isMoneyness ? tradingFormat.umpriceP.Format(Convert.ToDouble(d)) : tradingFormat.umprice.Format(Convert.ToDouble(d));
}
return string.Empty;
}
/// <summary>
/// 系统金额格式化
/// </summary>
public static string OtcFormatMoney(this double? d, bool grouping = false, int minDecimals = 2)
{
var sb = StringBuilderPool.Acquire();
sb.Append(grouping ? "#,##0" : "0");
if (minDecimals > 0)
{
sb.Append('.').Append('0', minDecimals);
}
var formatStr=sb.Release();
var v = d.HasValue ? d.Value.ToString(formatStr) : string.Empty;
// 处理v=="-0.00"问题
if (!string.IsNullOrEmpty(v) && double.TryParse(v, out double result))
{
if (result == 0)
{
v = 0d.ToString(formatStr);
}
}
return v;
}
/// <summary>
/// 系统金额格式化
/// </summary>
public static string OtcFormatMoney(this decimal? d, bool grouping = false)
{
var v = d.HasValue ? d.Value.ToString(grouping ? "#,##0.00" : "0.00") : string.Empty;
// 处理v=="-0.00"问题
if (!string.IsNullOrEmpty(v) && decimal.TryParse(v, out decimal result))
{
if (result == 0)
{
v = 0d.ToString(grouping ? "#,##0.00" : "0.00");
}
}
return v;
}
/// <summary>
/// 系统金额格式化
/// </summary>
public static string OtcFormatMoney(this double d, bool grouping = false, int minDecimals = 2)
{
var sb = StringBuilderPool.Acquire();
sb.Append(grouping ? "#,##0" : "0");
if (minDecimals > 0)
{
sb.Append('.').Append('0', minDecimals);
}
var formatStr = sb.Release();
var v = d.ToString(formatStr);
// 处理v=="-0.00"问题
if (double.TryParse(v, out double result))
{
if (result == 0)
{
v = 0d.ToString(formatStr);
}
}
return v;
}
/// <summary>
/// 系统金额格式化
/// </summary>
public static string OtcFormatMoney(this decimal d, bool grouping = false, int minDecimals = 2)
{
var sb = StringBuilderPool.Acquire();
sb.Append(grouping ? "#,##0" : "0");
if (minDecimals > 0)
{
sb.Append('.').Append('0', minDecimals);
}
var formatStr = sb.Release();
var v = d.ToString(formatStr);
// 处理v=="-0.00"问题
if (decimal.TryParse(v, out decimal result))
{
if (result == 0)
{
v = 0d.ToString(formatStr);
}
}
return v;
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this double? d, bool isPercent = false)
{
if (d.HasValue)
{
return isPercent ? tradingFormat.notionalP.Format(d.Value) : tradingFormat.notional.Format(d.Value);
}
return string.Empty;
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this decimal? d, bool isPercent = false)
{
if (d.HasValue)
{
return isPercent ? tradingFormat.notionalP.Format(Convert.ToDouble(d)) : tradingFormat.notional.Format(Convert.ToDouble(d));
}
return string.Empty;
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this double? d, int? CountRatio, bool IsUseDisplayNotional = false, bool isPercent = false)
{
if (d.HasValue)
{
d = IsUseDisplayNotional ? d.Value * (CountRatio ?? 1) : d.Value;
return isPercent ? tradingFormat.notionalP.Format(d.Value) : tradingFormat.notional.Format(d.Value);
}
return string.Empty;
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this decimal? d, int? CountRatio, bool IsUseDisplayNotional = false, bool isPercent = false)
{
if (d.HasValue)
{
d = IsUseDisplayNotional ? d.Value * (CountRatio ?? 1) : d.Value;
return isPercent ? tradingFormat.notionalP.Format(Convert.ToDouble(d)) : tradingFormat.notional.Format(Convert.ToDouble(d));
}
return string.Empty;
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this double d, bool isPercent = false)
{
return isPercent ? tradingFormat.notionalP.Format(d) : tradingFormat.notional.Format(d);
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this decimal d, bool isPercent = false)
{
return isPercent ? tradingFormat.notionalP.Format(Convert.ToDouble(d)) : tradingFormat.notional.Format(Convert.ToDouble(d));
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this double d, int? CountRatio, bool IsUseDisplayNotional = false, bool isPercent = false)
{
d = IsUseDisplayNotional ? d * (CountRatio ?? 1) : d;
return isPercent ? tradingFormat.notionalP.Format(d) : tradingFormat.notional.Format(d);
}
/// <summary>
/// 系统数量格式化
/// </summary>
public static string OtcFormatNotional(this decimal d, int? CountRatio, bool IsUseDisplayNotional = false, bool isPercent = false)
{
d = IsUseDisplayNotional ? d * (CountRatio ?? 1) : d;
return isPercent ? tradingFormat.notionalP.Format(Convert.ToDouble(d)) : tradingFormat.notional.Format(Convert.ToDouble(d));
}
/// <summary>
/// 系统百分比格式化
/// </summary>
public static string OtcFormatPercent(this double? d, int decimals = 2, bool grouping = false)
{
return d.HasValue ? OtcFormatPercent(d.Value, decimals, grouping) : string.Empty;
}
/// <summary>
/// 系统百分比格式化
/// </summary>
public static string OtcFormatPercent(this double d, int decimals = 2, bool grouping = false)
{
if (double.IsNaN(d) || double.IsInfinity(d))
{
return string.Empty;
}
if (decimals <= 0)
{
return d.ToString(grouping ? "P0" : "0%");
}
if (grouping)
{
return d.ToString("P" + decimals);
}
switch (decimals)
{
case 0: return d.ToString("0%");
case 1: return d.ToString("0.0%");
case 2: return d.ToString("0.00%");
case 3: return d.ToString("0.000%");
case 4: return d.ToString("0.0000%");
case 5: return d.ToString("0.00000%");
case 6: return d.ToString("0.000000%");
default: return d.ToString("0." + new string('0', decimals) + "%");
}
}
/// <summary>
/// 系统百分比格式化
/// </summary>
public static string OtcFormatPercent(this decimal? d, int decimals = 2, bool grouping = false)
{
return d.HasValue ? OtcFormatPercent(d.Value, decimals, grouping) : string.Empty;
}
/// <summary>
/// 系统万分比格式化
/// </summary>
public static string OtcFormatTenThousandsPercent(this decimal? d, int decimals = 2, bool grouping = false)
{
return d.HasValue ? OtcFormatTenThousandsPercent(d.Value, decimals, grouping) : string.Empty;
}
/// <summary>
/// 系统万分比格式化
/// </summary>
public static string OtcFormatTenThousandsPercent(this decimal d, int decimals = 2, bool grouping = false)
{
if (decimals <= 0)
{
return d.ToString(grouping ? "P0" : "0‱");
}
if (grouping)
{
return d.ToString("P" + decimals);
}
d = d * 10000;
switch (decimals)
{
case 0: return d.ToString("0‱");
case 1: return d.ToString("0.0‱");
case 2: return d.ToString("0.00‱");
case 3: return d.ToString("0.000‱");
case 4: return d.ToString("0.0000‱");
case 5: return d.ToString("0.00000‱");
case 6: return d.ToString("0.000000‱");
default: return d.ToString("0." + new string('0', decimals) + "‱");
}
}
/// <summary>
/// 系统百分比格式化
/// </summary>
public static string OtcFormatPercent(this decimal d, int decimals = 2, bool grouping = false)
{
if (decimals <= 0)
{
return d.ToString(grouping ? "P0" : "0%");
}
if (grouping)
{
return d.ToString("P" + decimals);
}
switch (decimals)
{
case 0: return d.ToString("0%");
case 1: return d.ToString("0.0%");
case 2: return d.ToString("0.00%");
case 3: return d.ToString("0.000%");
case 4: return d.ToString("0.0000%");
case 5: return d.ToString("0.00000%");
case 6: return d.ToString("0.000000%");
default: return d.ToString("0." + new string('0', decimals) + "%");
}
}
/// <summary>
/// 系统日期格式化
/// </summary>
public static string OtcFormatDate(this DateTime? date)
{
return date?.ToString("yyyy-MM-dd");
}
/// <summary>
/// 系统日期格式化
/// </summary>
public static string OtcFormatDate(this DateTime date)
{
return date.ToString("yyyy-MM-dd");
}
}
/// <summary>
///
/// </summary>
public enum OtcFormatFlag
{
/// <summary>
/// 标的价格(障碍价格)百分比格式值
/// </summary>
umprice,
/// <summary>
/// 标的价格(障碍价格)百分比格式值
/// </summary>
umpriceP,
/// <summary>
/// 百分比格式真实值
/// </summary>
umpricePR,
/// <summary>
/// 总额(PV)
/// </summary>
tradePrice,
/// <summary>
/// 单价(期权费,补偿金额)
/// </summary>
tradeSinglePrice,
/// <summary>
/// 名义本金
/// </summary>
StockEqvNotional,
/// <summary>
/// 期权费率(补偿费率)百分比真实数值
/// </summary>
premiumRate,
/// <summary>
/// 期权费率%(补偿费率%) 百分比格式
/// </summary>
premiumRateP,
/// <summary>
/// 交易数量(平仓数量)
/// </summary>
notional,
/// <summary>
/// 交易数量比例(平仓数量比例)
/// </summary>
notionalP,
/// <summary>
/// 希腊值
/// </summary>
greek,
/// <summary>
/// 2位精度百分比格式
/// </summary>
percent,
/// <summary>
/// 4位小数百分比格式
/// </summary>
percent4,
/// <summary>
/// 波动率
/// </summary>
volatility,
/// <summary>
/// 波动率百分比格式
/// </summary>
volatilityP,
/// <summary>
/// 预付金率
/// </summary>
marginRate,
/// <summary>
/// 预付金率百分比格式
/// </summary>
marginRateP
}
}
@@ -0,0 +1,171 @@
using Newtonsoft.Json;
using System.Runtime.CompilerServices;
using YLErp.Models;
namespace YLErp.Commons
{
/// <summary>
/// OTC Format
/// </summary>
public static class OtcFormatHelper
{
static OtcFormatModel _formatModel;
public static OtcFormatModel FormatModel
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _formatModel ?? (_formatModel = new OtcFormatModel()); }
}
public static void Initialize(string json)
{
if (!string.IsNullOrWhiteSpace(json))
{
_formatModel = JsonConvert.DeserializeObject<OtcFormatModel>(json);
}
}
#region----FormatValue----
/// <summary>
/// 四舍五入小数位
/// </summary>
public static double FormatValue(this double d, int precision, bool rounded = true)
{
if (double.IsNaN(d))
{
return 0;
}
if (rounded)
{
return Math.Round(d, precision > 0 ? precision : 0, MidpointRounding.AwayFromZero);
}
if (precision > 0)
{
var pow = Math.Pow(10, precision);
return Math.Truncate(d * pow) / pow;
}
return Math.Truncate(d);
}
/// <summary>
/// 四舍五入小数位
/// </summary>
public static decimal FormatValue(decimal d, int precision, bool rounded = true)
{
if (rounded)
{
return Math.Round(d, precision > 0 ? precision : 0, MidpointRounding.AwayFromZero);
}
if (precision > 0)
{
var pow = Convert.ToDecimal(Math.Pow(10, precision));
return Math.Truncate(d * pow) / pow;
}
return Math.Truncate(d);
}
/// <summary>
/// 四舍五入小数位
/// </summary>
public static double? FormatValue(this double? d, int precision, bool rounded = true)
{
return d.HasValue ? FormatValue(d.Value, precision, rounded) : d;
}
/// <summary>
/// 四舍五入小数位
/// </summary>
public static decimal? FormatValue(decimal? d, int precision, bool rounded = true)
{
return d.HasValue ? FormatValue(d.Value, precision, rounded) : d;
}
#endregion
#region----OtcFormat----
/// <summary>
/// 获取期权费总额格式转换
/// </summary>
public static double GetTradePriceDouble(double value)
{
return FormatModel.trading.tradePrice.FormatValue(value);
}
/// <summary>
/// 格式化权利金或补偿支付(百分数%和绝对值)
/// </summary>
public static string FormatPremium(bool? IsUsePremiumRate, double? percentValue, double? absValue)
{
return IsUsePremiumRate == true ? percentValue.OtcFormat(OtcFormatFlag.premiumRateP) : absValue.OtcFormat(OtcFormatFlag.tradeSinglePrice);
}
/// <summary>
/// OtcFormat扩展
/// </summary>
public static int GetMaxPrecision(OtcFormatFlag fmt)
{
switch (fmt)
{
case OtcFormatFlag.umprice:
return FormatModel.trading.umprice.GetMaxPrecision();
case OtcFormatFlag.umpriceP:
return FormatModel.trading.umpriceP.GetMaxPrecision();
case OtcFormatFlag.umpricePR:
return FormatModel.trading.umpricePR.GetMaxPrecision();
case OtcFormatFlag.tradePrice:
return FormatModel.trading.tradePrice.GetMaxPrecision();
case OtcFormatFlag.tradeSinglePrice:
return FormatModel.trading.tradeSinglePrice.GetMaxPrecision();
case OtcFormatFlag.StockEqvNotional:
return FormatModel.trading.StockEqvNotional.GetMaxPrecision();
case OtcFormatFlag.premiumRate:
return FormatModel.trading.premiumRate.GetMaxPrecision();
case OtcFormatFlag.premiumRateP:
return FormatModel.trading.premiumRateP.GetMaxPrecision();
case OtcFormatFlag.notional:
return FormatModel.trading.notional.GetMaxPrecision();
case OtcFormatFlag.notionalP:
return FormatModel.trading.notionalP.GetMaxPrecision();
case OtcFormatFlag.greek:
return FormatModel.trading.greek.GetMaxPrecision();
case OtcFormatFlag.percent: return 2;
case OtcFormatFlag.percent4: return 4;
case OtcFormatFlag.volatility:
return FormatModel.trading.volatility.GetMaxPrecision();
case OtcFormatFlag.volatilityP:
return FormatModel.trading.volatilityP.GetMaxPrecision();
case OtcFormatFlag.marginRate:
return FormatModel.trading.marginRate.GetMaxPrecision();
case OtcFormatFlag.marginRateP:
return FormatModel.trading.marginRateP.GetMaxPrecision();
default: return 2;
}
}
#endregion
}
}
@@ -0,0 +1,195 @@
namespace YLErp.Commons
{
/// <summary>
/// 系统格式化选项
/// </summary>
public class OtcFormatOption
{
private int _minDecimals;
private int _maxDecimals;
private string _format;
private bool _grouping;
private bool _rounded = true;
private bool _percent;
public OtcFormatOption()
{
}
/// <summary>
/// 格式化字符串
/// </summary>
public string GetFormat()
{
if (_format == null)
{
_format = NumberHelper.GetFormat(minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent, grouping: grouping);
}
return _format;
}
/// <summary>
/// 精度(旧字段当前使用minDecimals)(待删除)
/// </summary>
public int precision
{
get => _minDecimals;
set
{
_format = null;
_minDecimals = value < 0 ? 0 : value;
}
}
/// <summary>
/// 千分位分组(默认false)
/// </summary>
public bool grouping
{
get => _grouping;
set
{
_format = null;
_grouping = value;
}
}
/// <summary>
/// 是否四舍五入(默认true)
/// </summary>
public bool rounded
{
get => _rounded;
set
{
_format = null;
_rounded = value;
}
}
/// <summary>
/// 是否百分数(x%)(默认false)
/// </summary>
public virtual bool percent
{
get => _percent;
set
{
_format = null;
_percent = value;
}
}
/// <summary>
/// 最小小数个数
/// </summary>
public int minDecimals
{
get => _minDecimals;
set
{
_format = null;
_minDecimals = value < 0 ? 0 : value;
}
}
/// <summary>
/// 最大小数个数(小于MinDecimals时不起作用)
/// </summary>
public int maxDecimals
{
get => _maxDecimals;
set
{
_format = null;
_maxDecimals = value < 0 ? 0 : value;
}
}
/// <summary>
/// 非四舍五入情况下
/// </summary>
public string Format(double d)
{
if (double.IsNaN(d))
{
return string.Empty;
}
if (!rounded)
{
d = NumberHelper.Truncate(d, minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent);
}
return d.ToString(GetFormat());
}
/// <summary>
/// 非四舍五入情况下
/// </summary>
public string Format(decimal d)
{
if (!rounded)
{
d = NumberHelper.Truncate(d, minDecimals: minDecimals, maxDecimals: maxDecimals, percent: percent);
}
return d.ToString(GetFormat());
}
/// <summary>
///
/// </summary>
public double FormatValue(double d)
{
return OtcFormatHelper.FormatValue(percent ? d * 100 : d, maxDecimals > minDecimals ? maxDecimals : minDecimals, rounded);
}
/// <summary>
/// 该方法不可轻易修改,修改需确认
/// 此处用到该逻辑:return FormatModel.trading.premiumRateP < 1 ? "P4" : "P" + FormatModel.trading.premiumRateP;
/// </summary>
public override string ToString()
{
return $"minDecimals:{minDecimals}-maxDecimals:{maxDecimals}-percent:{percent}-rounded:{rounded}-grouping:{grouping}";
}
/// <summary>
/// 最大精度
/// </summary>
public int GetMaxPrecision()
{
return minDecimals < maxDecimals ? maxDecimals : maxDecimals;
}
public static implicit operator OtcFormatOption(int precision)
{
return new OtcFormatOption { minDecimals = precision };
}
}
/// <summary>
/// 百分比格式的otcformat
/// </summary>
public class OtcFormatOptionPercent : OtcFormatOption
{
public override bool percent => true;
public OtcFormatOption ToNonPercentOption()
{
return new OtcFormatOption
{
rounded = rounded,
grouping = grouping,
minDecimals = minDecimals + 2,
maxDecimals = maxDecimals + 2,
percent = false
};
}
public static implicit operator OtcFormatOptionPercent(int precision)
{
return new OtcFormatOptionPercent { minDecimals = precision };
}
}
}
@@ -0,0 +1,53 @@
namespace YLErp.Commons
{
public class ReturnInfo
{
public bool Success { get; set; } = true;
public string Msg { get; set; } = "";
}
public class ReturnInfo<T> : ReturnInfo where T : class
{
public T Content { get; set; } = null;
}
public static class Return
{
public static ReturnInfo Success(string msg = "")
{
return new ReturnInfo
{
Msg = msg
};
}
public static ReturnInfo<T> Success<T>(T content, string msg = "") where T : class
{
return new ReturnInfo<T>
{
Content = content,
Msg = msg
};
}
public static ReturnInfo Fail(string msg = "")
{
return new ReturnInfo
{
Success = false,
Msg = msg
};
}
public static ReturnInfo<T> Fail<T>(string msg = "", T content = null) where T : class
{
return new ReturnInfo<T>
{
Success = false,
Msg = msg,
Content = content
};
}
}
}

Some files were not shown because too many files have changed in this diff Show More