From 9d850a7723dc2ff398c0e8fc377a8e1a70679bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=94=A6=E9=BA=9F=20=E7=8E=8B?= Date: Thu, 27 Aug 2026 20:06:03 +0800 Subject: [PATCH] =?UTF-8?q?BugFix=201.ETF=E6=B2=A1=E6=9C=89=E6=9C=9F?= =?UTF-8?q?=E9=99=90=202.=E6=94=B6=E7=9B=98=E4=BB=B7=E5=92=8C=E5=87=80?= =?UTF-8?q?=E4=BB=B7=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DBModels/Consts/ConsMarginTerm.cs | 24 +--- .../margin_template_v2Controller.cs | 32 ++---- .../margin_template_v2ClientEdit.cshtml | 4 +- .../margin_template_v2DefaultEdit.cshtml | 4 +- .../margin_template_v2Edit.cshtml | 4 +- .../marginTemplateV2DefaultEdit.js | 108 ++++++++---------- .../marginTemplate/marginTemplateV2Edit.js | 92 +++++---------- 7 files changed, 94 insertions(+), 174 deletions(-) diff --git a/Framework/YLErp.Core/DBModels/Consts/ConsMarginTerm.cs b/Framework/YLErp.Core/DBModels/Consts/ConsMarginTerm.cs index 74779a4c..0105c168 100644 --- a/Framework/YLErp.Core/DBModels/Consts/ConsMarginTerm.cs +++ b/Framework/YLErp.Core/DBModels/Consts/ConsMarginTerm.cs @@ -10,33 +10,15 @@ namespace YLErp.DBModels /// /// 允许配置期限档(SpanConfig.BondTerm)的标的资产类型标志位。 /// 区间追保结构"按资产类型分类"时,仅这些类型允许设置非空期限档; - /// 本期仅含 利率债(TBonds=1<<4=16);ETF 子类分档能力见 TermTierEnabledEtfKinds。 + /// 仅含 利率债(TBonds=1<<4=16)。 + /// ETF 不分期限档(2026-08-27 业务裁定:ETF 没有期限概念, + /// 此前的 可转债 ETF/科创债 ETF 分档白名单 TermTierEnabledEtfKinds 已移除,如需恢复查 git 历史)。 /// public static readonly UnderlyingTypeEnum[] TermTierEnabledUnderlyingTypes = { UnderlyingTypeEnum.TBonds }; - /// - /// 允许配置期限档的 ETF 子类白名单(来自数据字典"ETF 子类")。 - /// 选择这些子类的 ETF 区块按固定4档展开(同利率债);其余子类/不区分的 ETF 区块单套参数、不分档。 - /// 取值与 underlying_manager.EtfSubType 字典项一致,由标的维护页维护。 - /// 注意:分档 ETF 子类行的 UnderlyingType 仍为 基金(32768),不走 TermTierEnabledUnderlyingTypes 位掩码(§1.2 决策:不再加枚举位)。 - /// - public static readonly List TermTierEnabledEtfKinds = new List - { - "可转债 ETF", - "科创债 ETF" - }; - - /// - /// 判断 EtfKind 是否允许分档(在 TermTierEnabledEtfKinds 白名单内)。 - /// - public static bool IsTermTierEnabledEtfKind(string etfKind) - { - return !string.IsNullOrEmpty(etfKind) && TermTierEnabledEtfKinds.Contains(etfKind); - } - /// /// 5年以下(同时也是兜底默认期限) /// diff --git a/YLErpWeb/Controllers/margin_template_v2Controller.cs b/YLErpWeb/Controllers/margin_template_v2Controller.cs index 591b8f45..ea788f5d 100644 --- a/YLErpWeb/Controllers/margin_template_v2Controller.cs +++ b/YLErpWeb/Controllers/margin_template_v2Controller.cs @@ -299,8 +299,8 @@ namespace YLErp.Web.Controllers } } - //区间追保结构 + 按资产类型分类时,期限档仅允许"允许分期限档的资产类型"(利率债)或"允许分档的 ETF 子类" - //(可转债 ETF/科创债 ETF,行 UnderlyingType=基金 + EtfKind=子类,不加枚举位)配置,防止非分档类型误配期限档; + //区间追保结构 + 按资产类型分类时,期限档仅允许利率债配置(2026-08-27 裁定:ETF 无期限概念, + //可转债 ETF/科创债 ETF 的分档白名单已移除,所有 ETF 子类/基金行一律单套参数不分档),防止非分档类型误配期限档; //该校验按行生效,与明细行数无关(单行明细同样拦截); //按严格掩码判定:行标的类型位必须全部落在可分档类型内(混合标志位如 利率债|信用债 配期限档同样拦截,与取数侧整行期限档过滤语义一致); //UnderlyingType 为空的通配行允许配期限档(取数侧期限档过滤在前、通配匹配在后,语义自洽) @@ -308,34 +308,24 @@ namespace YLErp.Web.Controllers && marginTemplate.UnderlyingSeperateType == (int)UnderlyingSeperateTypeEnum.CustomInstrumentType) { var enabledMask = ConsMarginTerm.TermTierEnabledUnderlyingTypes.Aggregate(UnderlyingTypeEnum.None, (a, t) => a | t); - var tierEnabledEtfKinds = ConsMarginTerm.TermTierEnabledEtfKinds; foreach (var detail in marginTemplate.Details) { var etfKind = detail.SpanConfig?.EtfKind; + var bondTerm = detail.SpanConfig?.BondTerm; //ETF 子类行:仅允许纯基金行(配置页子类选择器也只在纯基金区块出现) if (!string.IsNullOrEmpty(etfKind) && detail.UnderlyingType != UnderlyingTypeEnum.Fund) { throw new Exception("配置了 ETF 子类(" + etfKind + ")的参数组资产类型必须为 基金及基金专户"); } - var isTierEnabledEtfKind = tierEnabledEtfKinds.Contains(etfKind ?? ""); - if (!string.IsNullOrEmpty(detail.SpanConfig?.BondTerm) - && (detail.UnderlyingType & ~enabledMask) != UnderlyingTypeEnum.None - && !isTierEnabledEtfKind) + //ETF 子类不分期限档(对存量 4 档子类行回存给出精确报错;基金通配行由下面的掩码校验拦截) + if (!string.IsNullOrEmpty(etfKind) && !string.IsNullOrEmpty(bondTerm)) + { + throw new Exception("ETF 子类 " + etfKind + " 不分期限档,参数行不能配置期限档"); + } + if (!string.IsNullOrEmpty(bondTerm) && (detail.UnderlyingType & ~enabledMask) != UnderlyingTypeEnum.None) { throw new Exception("标的类型不允许配置期限档:" + UnderlyingTypeUtil.GetDesc(detail.UnderlyingType)); } - //分档 ETF 子类必须配期限档(区块固定4档),其余子类行禁配期限档 - if (!string.IsNullOrEmpty(etfKind)) - { - if (isTierEnabledEtfKind && string.IsNullOrEmpty(detail.SpanConfig?.BondTerm)) - { - throw new Exception("ETF 子类 " + etfKind + " 为分期限档类型,参数行必须配置期限档"); - } - if (!isTierEnabledEtfKind && !string.IsNullOrEmpty(detail.SpanConfig?.BondTerm)) - { - throw new Exception("ETF 子类 " + etfKind + " 不分期限档,参数行不能配置期限档"); - } - } } } @@ -394,8 +384,8 @@ namespace YLErp.Web.Controllers { if (marginTemplate.RuleType == (int)MarginRuleTypeEnum.区间追保结构) { - //区间追保结构:按 (利率债期限档, ETF细分) 元组分组校验(EtfKind 为预留扩展键,本期无 UI 写入,等价于仅期限档分组), - //同一分组内标的类型不允许重复,不同分组允许相同标的类型; + //区间追保结构:按 (利率债期限档, ETF细分) 元组分组校验——期限档仅利率债有值(其余恒空), + //EtfKind 为基金行的子类键;同一分组内标的类型不允许重复,不同分组允许相同标的类型; //通配行(空/全部,UnderlyingType=None/All)两两之间位与恒为 0 检不出,须单独拦截(BUG-09)—— //否则取数侧通配兜底 matched.First() 命中不确定 foreach (var termGroup in marginTemplate.Details.GroupBy(x => (x.SpanConfig?.BondTerm ?? "", x.SpanConfig?.EtfKind ?? ""))) diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml index 593f2612..fcb0b1ee 100644 --- a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml +++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml @@ -214,10 +214,10 @@ 参数组{{blk.no}}:资产类型:{{underlyingTypeNames(blk.ut)}} @*ETF 子类(仅纯基金区块展示):选 可转债ETF/科创债ETF 分期限档(展开4档),其他子类/不区分不分档*@  ETF 子类: - (按期限分档:≤5y、(5y-10y]、(10y-30y]、>30y,每个档位独立设置) + (按期限分档:≤5y、(5y-10y]、(10y-30y]、>30y,每个档位独立设置)

-

期限档位:{{sec.termLabel}}

+

期限档位:{{sec.termLabel}}