Commit Graph
100 Commits
Author SHA1 Message Date
hjhan 46cf7025a4 refactor(trace): 计息全路径埋点 + 关键日志常驻落盘
覆盖补全 CalcDailyCompoundInterest / CalcDailySimpleInterestByEod /
CalcDailyCompoundInterestByEod / InitSwapDealInterest 分支与复利跨期直取 /
orginPv 重映射 / preEod.id==0 播种 / ToRemainingClosePercent 语义翻转。
关键决策点改用 Critical() 无条件落盘(IYcLogger.Info, 类别 SwapCalc),
与开关解耦——出问题事后翻日志即可定位,不必事前开开关。
开关态(Header/Line/Day)仅作排障便利(?swaptrace=1 / 单测 Dump)。
纯增日志不改控制流;YLErpDAL 与 UnitTestProject 编译0错,
PrepaidPrincipal trace 测试 2/2 通过(SKIP_INITIALIZATION)。
2026-08-08 16:32:16 +08:00
hjhan 97029897c4 feat(swap): SwapCalcTrace 支持运行时按请求开关 + 响应头可见(无需重启)
- SwapCalcTrace 改为双模式:保留全局静态开关(单元测试用 IsEnabled/Dump),
  新增请求级 AsyncLocal 缓冲(EnableForRequest/DumpForRequest/ClearForRequest),
  由中间件按请求开关,不跨请求串扰。
- 新增 SwapCalcTraceMiddleware:开启方式三选一——URL ?swaptrace=1 /
  请求头 X-SwapCalc-Trace:1 / 标记文件 App_Data/swapcalctrace.on(无需重启);
  Response.OnStarting 时把逐日计息过程写入响应头 X-SwapCalc-Trace(超长落
  logs/swapcalctrace-YYYYMMDD.log 并在头给路径),同时写服务器日志。
  开发者在浏览器“网络”面板即可查看,零前端改码、零行为影响、性能可忽略。
- Program.cs 在 ExceptionMiddleware 后注册该中间件。
- 删除 CalcDailyCompoundInterestByEod 遗留垃圾日志 LogFactory.GetLogger("test").Error("lksafhasdhfjas")。
- CalcDailySimpleInterest 两处追踪守卫改为 IsEnabled||IsRequestEnabled。
- 既有 PrepaidPrincipalCloseTraceTest / ClosingChainTraceTest 2/2 仍全绿。

回答用户两点:① dynomicPrincipal 公式字段含义;② 原 Trace 需重启才能开,
现改为前端 otcdebug 式(?swaptrace=1) 即可在响应头看到逐步计息过程。
2026-08-08 15:57:00 +08:00
hjhan f6a5c1dc83 test(swap): 预付金腿收盘递推链trace——证明ValueDate正确推进到8.4(非公式bug)
- 新增 PrepaidPrincipalClosingChainTraceTest:纯内存驱动 SwapPositionCompose,
  从交易开始日 7.28 逐日收盘到部分平仓日 8.4(含 7.31互换/8.3重置/8.4平仓),
  捕获预付金腿每日 eod.ValueDate。
- 实测 ValueDate 逐日推进 7.28→7.31→8.3→8.4,证明收盘代码(DealInterests 四分支
  均 newEodPayPosition.ValueDate=settleDate)本身正确。
- 结论:8.7 平仓预付金端系统值 20.83(≈6天) 对应 eod.ValueDate≈8.1,说明该笔交易
  预付金腿 eod 在 8.1 之后未被收盘链重新生成(递推链在部分平仓日断了/收盘缺失滞后),
  属数据/流程问题而非利息公式 bug;收盘链若正常推进到 8.4,8.7 平仓应为 3 天=10.41(Excel)。
2026-08-08 15:46:54 +08:00
hjhan cd8c3b6c95 test(swap): 预付金腿两段式平仓计息过程暴露 + SwapCalcTrace追踪器
- 新增 SwapCalcTrace(零成本可开关追踪器,默认关闭,对生产零行为影响):
  在 CalcDailySimpleInterest 埋点,逐日记录计息起点/日终归档 ValueDate 地板/
  计息基数 dynomicPrincipal/当日利率/当日利息/累计,便于定位计息异常根因。
- 新增 PrepaidPrincipalCloseTraceTest:用截图参数(本金100000.23/利率2.1111%/
  8.4平40%→8.7全平)暴露计息过程,证明第二次平仓利息完全由日终归档 ValueDate
  决定——正确归档(ValueDate=8.4)+算尾=3天=10.41(Excel本次利息);
  错误归档(ValueDate=8.1)复现系统截图 6天=20.83。守卫1钉正确值10.41,
  守卫2复现20.83并证明 ValueDate 即『缺的要素』。
- 修正覆盖结论:浮动端/利息腿已有两段式覆盖,预付金腿两段式此前缺失(缺口
  与截图坏『预付金端』一行精确对应)。
2026-08-08 15:02:54 +08:00
hjhan fc01b766cd test(fe): 添加部分平仓比例刷新守卫 + 历史对账骨架(离线无DB) — closePercentInterestRefresh 守卫全部→部分平仓比例刷新链; historicalReconciliation 独立规格vs落库值差分; 全部18套件276测试通过 2026-08-08 14:16:54 +08:00
hjhan 096d2609eb test(swap): 为债券TRS期间结算自动互换10场景补单测 + SaveAutoSwapDeal接缝可测化
- 新增 BondTrsAutoSwapScenarioTest:覆盖测试场景/测试场景-0702复测两页
  10个自动互换场景(含付息日历登记日2/28→支付日3/2、资金发生日3/3/3/4)、
  部分平仓后按登记日持仓数量计算(剩余60%为54240)、同日多腿合并、
  分红支付日偏移、无结算观察日不得凭空产生互换等,共11条断言全绿。
- SaveAutoSwapDeal 改为走 AddClientCash/AddSwapEvent/PersistFlowEvent 接缝
  (默认实现与重构前行为一致,零行为变更);UpdateInitalPostion 由 private
  改 protected virtual;GetPreDealDate 改 virtual,便于无DB单测。
2026-08-08 13:32:22 +08:00
hjhan 5ca297a73c test(swap): 补充固定值腿业务场景4-2两段式平仓覆盖(GLMS-债券TRS期间结算260702缺口)
- 新增 业务场景4_2_固定值腿_部分平仓后再全平_两段利息均随比例线性缩放
  覆盖「国联民生-债券TRS期间结算功能测试260702.xlsx」Sheet1 矩阵中唯一标记为
  「未测」的格子:固定值腿(mode=1) 部分平仓一次后隔数日再全平。
- 断言两段利息各自随平仓比例线性缩放(30%/70%对照同日期全平),守护 d1badfe4
  的固定值腿平仓比例缩放修复;修复前 newClosePercent=1 会让部分平仓利息等于全平。
- 现有利息测试 75 例全绿(场景1/2/3/4 + 单笔 + 固定值腿 + 多步守恒)。
- 0601 缺陷测试-利息(场景1-4 + 单笔0007/0006) 与 20260807 同源,已由上述套件覆盖。
2026-08-08 12:26:27 +08:00
hjhan 68a3f0b4fa debug(swap)+test: 平仓利息 otcdebug 埋点 + 修复3个前端红测试
1) otcdebug 埋点(unwindSwapTrade.js, 仅 ?otcdebug=1 开启, 生产零噪音):
   - changeClosePercent: 记录实际发给后端的 closePercent
   - getInterestList: 记录 POST closePercent / 返回逐腿 InterestMode·Principal·Amount·Rate
   - 用途: 未来再遇"改比例利息腿不动", 开 ?otcdebug=1 即可定位前端没传对还是后端没缩放

2) 修复分支上既有3个红测试(均为配置/生产已改、测试未跟上或契约过时):
   - swapPrecisionConfig.test.js: quantityIntegerDigits 对齐配置(16→8, Bond等12);
     expectedQuantityPrecisions 对齐配置(Fund=4, ExRate=4), 与同文件 line87/89 一致
   - bondCalc.integration.test.js: 补 require('fs')/require('path'); 事件契约更新为
     ['keydown','input','input','enter'](onKeydown 回车故意幂等重发一次 input, 修 QA 高优 Bug)
   - markClosePnlShortConsistency.test.js: 空头一致性测试由 RED 转 GREEN(income 已补 longRatio);
     CASE deliveryPrice 105→1.05 与 GOLD 的 105*scale(0.01) 单位对齐

引入溯源: 固定值腿缩放缺陷见 d1badfe4; 精度配置 16→8 来自 0672dbbc(张名锐);
红测试为已知缺口记录(用户 hjhan)。
2026-08-08 12:18:45 +08:00
hjhan d1badfe48f fix(swap): 固定值利息腿平仓比例缩放缺陷(GLMS) + 证明测试
- SwapDealService.CalcNotionalByMode 固定值分支原实现把 closePrincipal 钉成 Fix 全量
  且 newClosePercent 强制置 1,导致界面『全部平仓→部分平仓』改比例时利息腿数据
  完全不变。修复为 closePrincipal = InterestPrincipalFix * closePercent(与预付金/
  多头/空头/标的期初全价等模式一致),posiPrincipal 仍保留 Fix 全量,newClosePercent
  保留真实平仓比例,两个缩放入口作用于不同项不双重缩放。
- EOD 结算路径 SwapEodPositionService 传 closePrecent=1 字面量,Fix*1=Fix,日终行为不变。
- 新增 SwapFixedInterestLegClosePercentBugTest(9 例真实函数复现+证明)。
- 纠正 SwapUnwindPrepayPrincipalBugTdd 中把『免疫』当期望的断言为线性缩放。
- 引入溯源:95686f4c(吴方海, EQD-5718, 2026-05-08);newClosePercent=1 祖传自 acbd2e76(2024-06-04)。
2026-08-08 10:34:10 +08:00
hjhan 994429d3b7 test(interest): 补充场景1固定/浮动+场景2+单笔GLMS利息回归测试
- SwapInterestScenario1And2Test: 场景1浮动(8,利息=0) + 场景1固定(12) + 场景2第一重置期内平仓(12),共32例
- SwapSingleTradeVerificationTest: 单笔GLMS-0007/0006 第三重置期/到期平仓 + 30%+70%守恒,共4例
- 全部走真实生产函数(GetInterests / SaveAutoEodWithCloseInterestPosition),对照 Excel oracle(AO/BL/BN/累计利息)
- 与场景3/4 合计 60 例全绿(容差0.01),非 re-baseline
2026-08-08 09:31:43 +08:00
hjhan feffc196c6 fix(interest): 修复浮动利率部分/全平利息尾差(Bug A/B/C) 并补充回归测试
- Bug A(SwapDealService): 复利早退分支 TdInterestAmount 未乘 closePrecent
- Bug B(SwapDealService): CalcDailyCompoundInterest 用 interest 而非 EOD 四舍五入快照
- Bug C(SwapEodPositionService): 用 oriPosiNotionalValue(平仓前原始本金) 重算平仓利息
- 新增 SwapInterestScenario3And4FloatingTest: 24 个浮动利率用例, 走真实生产函数
  (GetInterests / SaveAutoEodWithCloseInterestPosition), 对照 Excel 手算 oracle(AO/BL/BN),
  断言容差 0.01, 非 re-baseline; 含 DebugCompare 调试输出便于与生产/Excel 逐项对比
- 附 缺陷分析-利息部分平仓尾差20260807.md 与 oracle 源 xlsx
2026-08-08 09:02:05 +08:00
hjhan 66a97e03ef fix(swap): 复利重置日=平仓日时 calcLast 不再跳过 FR007 取价
根因(GLMS-JIATT-20260805):InterestCalcMode='10'(算头不算尾,calcLast=false)时,
CalcDailyCompoundInterest 循环的 'if(!calcLast && accrueDate==endDate) continue'
会跳过平仓日。若平仓日恰好是重置日(i%period==0),取价代码块被一并跳过,
导致 flowEvent.FloatRate 落库为旧周期利率,传染后续 EOD 复利计算。

修复:把重置日的 FR007 取价提前到 calcFirst/calcLast 跳过判断之前——
calcLast 只应跳过'计息',不应跳过'重置日利率取价'。同时循环外用最终
floatRate 兜底赋值 flowEvent.FloatRate,确保落库值反映最后重置日的利率。

验证:
- CI_007 合成测试(平仓日=重置日):修复前 FloatRate=旧值(FAIL),修复后=新值(PASS)
- 连库验证(GLMS-JIATT-20260805 8/4平仓):FloatRate 0.0123→0.0213(8/3新值)
- 利息金额不变(calcLast 不计当天利息,Amount 不受影响,只修正 FloatRate 字段)
- 全套 swap 测试无新增回归(334通过,2失败均为pre-existing单利/EOD路径)

附:CI_007 复现测试 + GLMS20260805 FR007/EOD 诊断工具 + 文档 longRatio 状态更新
2026-08-07 20:43:16 +08:00
hjhan 75b033dd7b test(swap): 添加 GLMS-JIATT-20260805 复利部分平仓利息诊断测试
连 96 测试库的诊断工具,用于定位'100%平仓 vs 部分平仓利息差异异常'。
对同一笔交易分别调 GetUnwindInterests(100%) 和 (40%),逐腿打印本金/利息并对比,
输出比值以判断是否线性。复现并确认了 consumedInterest 未按 closePercent 缩放的根因
(已由 a0be0eb0 修复,本测试作回归诊断保留)。

模式同 GLMS20260701DbDiagnoseTest:TestCategory(DbDiagnose),连不上库自动 Inconclusive。
2026-08-07 19:00:31 +08:00
hjhan 52b665cd39 fix(otcformat): /front/otcformat 改为 NoStore,修复保存配置后缓存不生效
FrontController.OtcFormat 原使用 ResponseCache(Duration=600, Location=Any),
导致在 Admin 配置页保存 OtcFormat 后,前端最长 600s 内仍返回旧内容,
表现为'改了配置前端不起效果',需手工加 ?v= 才能破缓存。

改为 NoStore 后保存即对所有用户立即生效。
同时在 FrontController 类注释补充完整排查文档:
- 三个同名 otcformat.js 文件的角色与路径
- DB AppConfig 真相源(PGroup=ProjectConfig, PName=OtcFormatConfig)
- 写入/读取/启动三条数据流
- '前端不起效果'的 4 步排查顺序
- 多节点落盘只写当前节点的坑

OtcConfigController.AjaxSaveOtcFormat 补充多节点陷阱说明并指向 FrontController 文档。
2026-08-07 17:53:48 +08:00
hjhan acc37bdb99 test(swap): 补算尾/不算尾部分平仓的 InterestIncomeSum 递推断言
DI_MANUAL_PARTIAL_CLOSE_CalcLast/NoCalcLast 原仅断言 TdInterestIncome(当日新计利息
按全额/剩余本金计提),未钉死该差异是否传导到待实现利息总额 InterestIncomeSum。
补一条递推断言:InterestIncomeSum = 前日待实现 + 当日新计 - 当日实现,
锁定"算尾 → 被平仓部分多1天利息 → 进入待实现总额"完整链条,不算尾同理对照。

纯测试改动,生产代码无改动,双绿通过。
2026-08-07 15:43:11 +08:00
hjhan 7e2042510f docs(swap): 记录 income 页当前行为与待确认项
- 第七章开头加状态说明:'income 漏 longRatio 即错' 标注为待业务背书的
  Working Hypothesis,非最终定性。
- 新增第八章'当前行为实录与待确认项':
  * 8.1 income(收益结算按钮)页 MarkClosePnl 当前公式实录(无 longRatio,代码实证)
  * 8.2 swap_position.PosiGrossPrice 恒为期初价 P0、结算/平仓后不滚动(代码实证)
  * 8.3 已确认事实 vs 待确认假设 对照表
  * 8.4 待业务/量化确认的 4 个问题清单
  * 8.5 两条验证 SQL(空头结息事件 / 同持仓多次结算)
  * 8.6 多轮回归未暴露原因(覆盖空洞+校验同源+数据隐形+多头恒等)
- 本次仅记录现状与待确认项,不改代码、不加测试;longRatio 不一致与
  基准不滚动的真实生产影响仍为待证(需跑 8.5 SQL 定性)。
2026-08-07 15:26:54 +08:00
hjhan b3ce94b67b test(swap): 修复 SPC_007 红灯并锁定 a1cdb2cd 日期分桶修复
a1cdb2cd(修正平仓事件日期过滤条件)将 ResolveInterestLegPositionsAsOf 的分桶从
EventDate 改为 UnwindDate,方向正确(应按经济生效日而非簿记日),但 SPC_007 的测试
事件未设 UnwindDate(null) → 落入空桶 → 断言 Expected7000 Actual10000 红灯。

改动(仅测试,生产代码零改动):
- SPC_007:给 close/floatClose 补 UnwindDate(与 EventDate 同日,保持原语义),让测试
  数据贴近真实生产事件(平仓事件总会设 UnwindDate),红灯转绿
- 新增 SPC_008:EventDate≠UnwindDate 场景(簿记 7/11 滞后于经济生效 7/9),验证按
  UnwindDate 分桶——锁定 a1cdb2cd 的修复价值。已反向验证:临时回退 a1cdb2cd 后 SPC_008
  立即失败(Expected7000 Actual10000),证明该测试有效,能抓住此类修复被回滚

双绿通过。生产代码无改动。
2026-08-07 11:39:08 +08:00
hjhan 1bf517188c fix(swap): 待结算分红收益(DividendPending)改用 EOD 全量口径,修复 e3c473ba 的硬0回归
e3c473ba 把前端 DividendPending 硬编码 0,对 GLMS-20260105-0004(PosiDividendSum 恰好=0)
测试通过、掩盖问题。但对 PosiDividendSum≠0 的部分平仓交易,硬0 会落库
(SwapFlowEventService:588 冲账取负写入 swap_flow_event.DividendPending)并在事件列表
"待结算分红收益"列显示错误的 0——是确定的回归。

口径论证(代码证据,非拍板):DividendPending(待结算存量) ↔ EOD PosiDividendSum(未实现),
字段名、递推公式(SwapEodPositionService:1825 已扣当日实现)、列标签(SwapflowList:561)、
旧前端意图(posiQty×totalInterest 想表达"平仓后剩余")四处同指全量口径。是存量非流量,
不按本次平仓比例分摊。

改动:
- SwapDealService.InitUnwind:GetPreEodDividendSum 返回值取一次,同时喂 DividendIn 与
  DividendPending(避免重复查库);GetPreEodDividendSum 注释补口径论证+历史教训(防后人改回硬0/分摊)
- 前端 unwindSwapTrade.js:删除 DividendPending=0 硬写,保持后端值不动
- 前端 incomeSwapTrade.js:DividendPending 保持 0(互换=全量结清,结清后待结算归0),
  注释说明与平仓页口径差异
- 测试补非0场景:GLMS-20260706-0004(PosiDividendSum=-150,000)验证返回非0真实值,
  直接针对"硬0被0掩盖"盲区,防止该回归复现

全量构建 0 错误,4 个分红测试全绿(含 0 场景、互换页、无EOD防御、非0场景)。
2026-08-07 09:28:18 +08:00
hjhan e3c473bafd fix(swap): 分红前部分平仓过的交易,后续平仓/互换分红收益改由 EOD 单一可信源提供
问题:GLMS-20260105-0004 在 2/28 部分平仓 40%、3/2 登记日互换后,3/3 继续平仓/互换时
"浮动端平仓盈亏·分红"误显 -36,160(应为 0)。根因:前端 getDivindIn 用期初全额持仓
×totalInterest 重算分红(-90,400),扣除 consumedDividend(-54,240) 后余 -36,160,
恰为已平仓 40%(登记日前已不持有、不享有该笔分红)部分被重复计入。

方案C:分红收益改由后端读上一收盘日 eod_swap_position.PosiDividendSum 提供(单一可信源),
前端不再自算。EOD 的 PosiDividendSum 已按"实际持仓递推 + 当日实现扣除"准确算出待实现分红,
3/2 互换后为 0。因手动平仓/互换后端不重算 DividendIn、直接落库前端值,后端 Init 阶段给出
正确值可同时修正展示与落库。

改动:
- SwapDealService 新增 GetPreEodDividendSum(复用 GetUnwindInterests:624 的上一 EOD 日期推导
  + SwapEodPositionService.GetPreEodPositions),protected virtual 便于测试覆写、无 EOD 返回 0
- InitUnwind/InitIncome 的 floatEvent.DividendIn 从硬编码 0 改为 GetPreEodDividendSum 返回值
- unwindSwapTrade.js/incomeSwapTrade.js getDivindIn 删除 GetBondPayMentInterest 调用与全部自算,
  保留后端 DividendIn 值、DividendPending=0
- GetBondPayMentInterest 加废弃文档注释(保留接口供历史调用,consumedDividend 无日期过滤隐患随废弃消解)

测试:GLMS20260105PartialCloseDividendBugTest 连真实库验收——3/3 平仓/互换 DividendIn=0(修复前
-36,160)、无 EOD 防御返回 0。全量构建 0 错误。
2026-08-06 19:09:45 +08:00
hjhan 2dcd17f557 EQD-6967 fix(eod): 收盘缺失价检查按客户作用域收敛持仓分支
- EodCheckSettlePrice 抽出 public static QueryPositionUnderlyingCodes,
  增加 clienIds 过滤:client-scoped 收盘不再把其他客户/系统级(ClientId=0)
  持仓标的纳入"检查标的结算价格缺失"。
- clienIds==null(系统级全结算)时短路为 true,零回归。
- class 改 public 便于单测。
- 新增 EodCheckSettlePriceClientScopeTest 确定性回归测试(红绿双段验证)。
- 代码补充 EQD-6967 防御行为详细注释(仅聚焦客户作用域收敛)。
- 防御范围说明:场内 exchange_trade 无 ClientId 列,场内分支噪声需靠结算模式选
  '场外交易'(IsSettleExchangeTrades=false)规避,本提交不覆盖该分支。
2026-08-06 13:56:31 +08:00
hjhan c1c2ed9dda EQD-6979 TRS合约数据实时推送 2026-08-06 10:58:44 +08:00
hjhan 41de2f6dd6 fix: 放宽 MaxModelBindingCollectionSize 至 1000000 修复互换观察日区间超限报错 2026-08-05 12:29:54 +08:00
hjhan ca74095e0c fix(sac): 修复预览页序列化 SacInfo 时 TypeId 触发 IsGenericParameter 异常
- SacDescriptionAttribute 增加 ShouldSerializeTypeId() 返回 false,
  跳过 Attribute.TypeId(System.Type)的序列化,避免 Newtonsoft 抛
  "Method may only be called on a Type for which Type.IsGenericParameter is true."
  (13.0.1 安全版对 Type 成员序列化行为变更后暴露)
- ExceptionMiddleware 改用 exception.ToString() 记录完整异常(类型+
  内层 message+堆栈),替代仅取最内层 message,便于定位此类反射异常根因;
  该信息仅写入日志,不返回前端
2026-07-31 09:10:18 +08:00
hjhan a871bcb9fe fix(login): 登录页 NeedCaptcha=false 时 CaptchaClick 避免 null.setAttribute 报错
页面加载时无条件调用 CaptchaClick(),但 <img id="captcha"> 仅在
NeedCaptcha=true 时渲染,导致无需验证码时 getElementById 返回 null,
null.setAttribute 抛错。增加 if (!img) return 守卫,img 存在时行为不变。
2026-07-31 09:10:05 +08:00
hjhan 74c434bdd8 fix(swaptrade): 修复重输/重贴原值后回车不触发计算、粘贴不标记REV的静默失效
- swapPricePrecisionHelper: onKeydown 回车时无条件派发 input+enter,
  不再依赖仅值变化才触发的原生 change;onPaste 粘贴即派发 input 标 REV;
  onChange 不再派发 enter(改由 onKeydown 唯一派发,避免重复计算)
- 新增 fe-tests/swapPriceInput.component.test.js 回归守卫(6 用例,
  回退旧版即变红,锁定该 QA 高优 Bug 修复)
- bundle/bundleV2: otcDebug 调试开关改为 localStorage 持久化 +
  URL ?otcdebug=1 触发,便于排查价格计算链路
2026-07-31 09:07:08 +08:00
hjhan 52c11e7540 fix(diag): 简化 otcDebug 调试开关命名并支持 localStorage 持久化
- __otcDiag 双源对象拆为 __diag(版本元数据, 仅供 banner 展示) + __debug(调试开关布尔值, 唯一真相来源), 消除 __私有对象 与 otcDebug.API 大小写混搭。
- __debug 解析顺序: URL ?otcdebug=1(命中即写回 localStorage) -> localStorage -> 兜底 __diag.debug。同源父子 frame 共享 localStorage, 使 _InfoLayout iframe 弹窗(新增互换交易)自动继承父页面开过的调试开关, 修复弹窗内无调试日志。
2026-07-30 14:42:10 +08:00
hjhan 7a9702c707 fix(diag): JsVersion 纳入前端文件 mtime,修复纯前端改动缓存不刷新
根因(?otcdebug=1 排查时暴露):JsVersion 只取 bin/YLErp*.dll 的 mtime,
纯前端改 wwwroot/Scripts 而不重编 DLL 时,?v= 缓存戳不变,浏览器仍加载旧版
——即"发布了但不更新"。

修复:
- JsVersion = max(DLL mtime, wwwroot/Scripts/** mtime, wwwroot/Statics/bundles/** mtime)
- wwwroot 定位用 Directory.GetCurrentDirectory()(与 StaticUrlMiddleware 一致,
  开发=项目根/部署=publish 目录),目录不存在则回退仅 DLL mtime
- BinFileVersion 保持原义(仅 DLL,用于诊断显示后端构建时间)

附带修复 git 字段:AssemblyInformationalVersion 无 +sha(如固定版本 1.0.0)时,
回退从 .git/HEAD 读取当前 commit 短 sha,不再误显示 '1.0.0'

验证:dotnet build 0 错误
2026-07-30 11:25:11 +08:00
hjhan c519dcf44f fix: otcDebug 开关在 _InfoLayout(TradeEdit)页面也能从URL读取
根因(用户反馈:开了 ?otcdebug=1 但输入/计算无任何 console 输出):
1. 用户实际在 /swapTrade2/TradeList 页面——该页不加载 swapTradeEdit.js(埋点所在),
   只加载 swapTradeList.js(零债券代码),故三字段 trace 永远不会执行。
2. 即使跳到 TradeEdit 页面,_InfoLayout.cshtml(TradeEdit 用的 layout)
   未灌入 window.ylotc.__diag,导致 main.js 中:
     var __otcDiag = (window.ylotc && window.ylotc.__diag) || { debug: false };
   → debug 永远 false → 所有 otcDebug.log 全部空转。

修复:main.js 的 __otcDiag 初始化改为双源读取——
  优先用 _MainLayout 灌入的 ylotc.__diag(含 jsVersion/git/built 元数据);
  若 layout 未灌入(_InfoLayout 等),则从 ?otcdebug=1 / localStorage.otcdebug 直接读,
  确保 debug 开关在所有页面生效。

测试:diag 18 + bondCalc 81 = 99 例全过。
注意:此改动在 main.js 中,需重建 bundle.js 才能生效(dotnet publish 自动触发)。
2026-07-30 10:30:31 +08:00
hjhan 72b2a4cbf6 fix: 债券三字段调试日志改为 otcdebug gated,出问题时一键可定位
用户指出:上一笔直接删了 onBondPriceKeydown 的逐键 console.log 又没补 gated 版,
等于平时没噪音了、真出问题也看不见——这正是反复改几十遍都满足不了需求时才需要日志兜底的场景。

修复:
- onBondPriceKeydown 逐键 before/after(driver/rev) trace 改为 otcDebug.log(?otcdebug=1 才输出)
- onBondPriceEdit(失焦/粘贴) 补同款 gated trace
- calcBondForItem 补 enter/success/calc-error/post-fail 四条 gated trace
  → 三字段整条链路(逐键→回车→计算器→源/AUTO或失败)开开关即得完整现场
- main.js 第226/669行 catch 内 error console.log 保留(错误日志本就应在生产可见,利于定位)
- otcDebug 机制本身已存在且唯一:?otcdebug=1 或 localStorage.otcdebug=1 开启,
  _MainLayout 注入 jsVersion/git/built,加载时打带版本 banner,平时零噪音

测试:
- diag.test.js 新增源码守卫:swapTradeEdit.js 三字段埋点必须 otcDebug.log、不得退回裸 console.log
- diag 18 例 / bondCalc 81 例全过
2026-07-30 10:15:00 +08:00
hjhan ead2c01f0d fix: 债券三字段 keydown 守卫改用正向白名单,堵住"没改值却标REV"的缺口
根因:onBondPriceKeydown 的按键排除列表只排除回车/Ctrl-Alt-Meta/Tab/Home/End/←→/F5,
漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F1-F4,F6-F12/纯Shift。
这些键不改变数值却会触发 applyBondManualEdit 标 REV——正是用户观察到的
"鼠标放输入框没改值、只按了方向键/功能键却有时出现REV"。

修复:
- swapCalc.js 新增纯函数 isBondPriceValueKey(event):正向白名单(数字/小数点/减号/Backspace/Delete)
  才返回 true,其余(回车/修饰键/导航键/功能键/纯Shift)返回 false,jest 可测。
- swapTradeEdit.js onBondPriceKeydown 改用该函数判定,并移除两行每次按键的 console.log 噪音。
- 纯鼠标聚焦(无按键)本就不会标REV(无 focus/click 处理;vue-number-input 的 input 仅在值变化(失焦/回车)触发),
  本次仅修正按键守卫缺口。
- 同步补 9 个单测覆盖各类按键(响应"最好单测阶段解决")。

约定1/约定2/约定3 累加REV 行为不受任何影响(仅改判定是否标REV的触发条件)。
测试:bondCalc 全套 81 例通过。
2026-07-30 10:06:42 +08:00
hjhan c97d7e7995 feat(diag): 可保留调试日志+版本可追溯机制(阶段0.5止血增强)
解决 EQD-6838 排查时硬编码版本号 v20260729a 散落3处、无法按需开关、
看不到实际加载缓存戳的问题。

层1 运行时开关(核心):
- HtmlUtil.cs 新增 GitCommit(读 AssemblyInformationalVersion,零新依赖)
- _MainLayout.cshtml 注入 window.ylotc.__diag(jsVersion+git+built)
- main.js 顶部新增 otcDebug 工具(banner+log),debug 默认 false 生产零输出
- swapTradeEdit.js / fastVue.base.js 硬编码标记改为 otcDebug.banner
- 开启方式:URL ?otcdebug=1(会话)或 localStorage.setItem('otcdebug','1')(持久)

层2 构建守卫:
- pre-commit 新增第三块 bundle 版本一致性检查(rebuild-bundles.py --verify)
- 修复 Windows python3 Store stub 不可用问题(优先用 python)
- 注:bundle 头部不注入 git sha(会随 commit 变化导致 verify 永久失败),
  版本可追溯完全由运行时 __diag(后端注入)覆盖

层3 单测预防:
- 新增 diag.test.js(213测试之一),用 console spy 守卫开关行为
- 断言不再含硬编码 v20260729a + 防止第二套调试开关回归

验证:11 suites/213 tests 全绿,--verify 通过,C# 编译0错误
2026-07-30 10:06:21 +08:00
hjhan 0747deb909 fix: 步骤3-b 多字段连续手动编辑应累计REV(不再仅最后赢)
根因:applyBondManualEdit 每次调用先重置全部 bondRev 再只标当前字段,
导致连续手动编辑多字段(不回车)时只有最后编辑的字段留 REV,
中间手工编辑字段的 REV 被静默抹掉,UI 与步骤3-b需求不符。

修复:applyBondManualEdit 改为累计 REV(仅置当前字段、保留其它已手工编辑字段的 REV),
driver/AUTO 仍清。单字段编辑(步骤2/3)行为不变,无回归。

配套:
- 更新 a2d2bb31 中3个旧断言("仅最后留REV")为断言累加,避免测试守护错误行为
- 文档约定3 补充"连续编辑多字段时各手动字段累计REV"

swapCalc.js 为独立 script(?v=JsVersion),部署 dotnet publish 即生效,无需重建 bundle。
测试:bondCalc 全套 72 例通过。
2026-07-30 09:55:57 +08:00
hjhan a2d2bb31ea test: 补齐步骤3-b及多字段组合场景的债券三字段测试覆盖
文档(互换债券三字段互算踩坑总结与测试指南.md)列出的步骤3-b
(不敲回车连续编辑多字段)此前无任何测试断言最终落标状态。

新增 7 个用例(35->42,总计72->79):
- Step3-b: 回车CP->改DP->改YD(均不回车) -> 仅YD留REV
- Step3-b变体: 连续编辑CP->DP->YD -> 仅最后赢
- 三字段轮换(CP->DP->YD->CP) -> 仅最终CP留REV
- 步骤4-1: 回车CP->回车DP->改YD -> 多源降级为单REV
- 步骤4-1变体: 三字段都回车后改CP -> 仅CP留REV
- 数值保留守卫: 多次applyBondManualEdit不联动修改任何价格值

核心发现:applyBondManualEdit每次重置全部bondRev再只标当前字段,
故多字段连续编辑时只有最后一个字段保留REV是确定性行为,
现已用测试钉死,防止未来重构破坏此语义。
2026-07-30 09:22:12 +08:00
hjhan ad16095729 docs: 更新改进方案——补充双bundle架构说明+qiankun微前端现状+阶段0已完成标记 2026-07-29 22:47:10 +08:00
hjhan d380ae3f28 docs: 新增 zszq-trs 接手统一总览(技术栈地图/高频优先级/6大坑/学习路径/文档复核/微前端盲区) 2026-07-29 22:46:42 +08:00
hjhan 6b32f254b1 test(fe): 阶段0止血——pre-commit守卫+postSafe+jest.config+CI脚本+otcformat精度修复 2026-07-29 22:33:45 +08:00
hjhan daaf993126 docs: 互换模块长期改进方案——从106次提交47%修复率到可预测交付 2026-07-29 22:13:28 +08:00
hjhan ffa51c6ae6 test(EQD-6838): 补充债券三字段互算集成层测试 + 踩坑总结文档 2026-07-29 21:28:07 +08:00
hjhan 56256cad0b [EQD-6838] 补充踩坑注释:main.post 业务错误走 reject、本文件独立 script 部署、bundle 大小写敏感+非致命
- base/main.js __post/main.post:标注业务错误(success=false/errcode)与网络异常均走 deferred.reject,
  调用方只挂 .done 会静默跳过失败分支(约定2补充曾因此长期不生效)。
- app/swaptrade/swapTradeEdit.js calcBondForItem:标注本文件为独立 <script>(不在 bundle.js)、须 dotnet publish
  刷新 JsVersion 缓存键才生效;并展开 .fail 注释记录约定2补充一度不生效的历史根因。
- rebuild-bundles.py:标注 bundleconfig 输入路径在 Linux 大小写敏感(曾 MISSING 打挂 publish)、
  缺失输入必须非致命(否则 exit 1 连带打挂 dotnet publish)。纯注释,无逻辑改动。
2026-07-29 19:51:42 +08:00
hjhan ce4ef62950 [EQD-6838] 修复约定2补充:计算器算不出时另两字段清空+三标识全清(main.post reject 未走 .done)
- 根因:main.__post 在 resp.success===false(业务错误) 时 deferred.reject,而 calcBondForItem 仅挂 .done,
  导致业务错误/网络异常分支整段被跳过,applyBondCalcFailure+syncBondFlags 不执行,约定2补充静默失效。
- 修复:main.post 链补 .fail 处理 reject(业务错误+网络异常),统一在此清空非源字段+清 源/AUTO/REV 标识;
  另为两个 pre-POST 失败分支(源为空、开始日缺失)补 syncBondFlags,使视图刷新。
- 错误文案仍由 __post 的 alertFn(main.message) 非阻塞提示,不重复弹。纯函数 applyBondCalcFailure 逻辑不变。
2026-07-29 19:44:10 +08:00
hjhan 7c0572ade8 fix(EQD-6838): 约定3 按键清标识加排查日志+forceUpdate兜底
1. syncBondFlags 改用新对象引用({} 字面量)确保 Vue 2 响应式触发
2. 加 \ 兜底,防止 \ 因相同值未触发更新
3. onBondPriceKeydown 加 console.log 排查日志,确认事件是否触发

用户反馈:版本标记 v20260729a 已加载、重算按钮已消失,
但约定3(删字符不消失标识)和约定2补充(计算错误不清零)仍未生效。
加日志确认 keydown 事件是否触发、状态值是否正确。
2026-07-29 19:31:59 +08:00
hjhan 8124bb46bb chore(EQD-6838): 加版本标记日志用于排查 Jenkins 部署是否加载最新代码
在 swapTradeEdit.js 和 fastVue.base.js 开头加 console.log 版本标记。
F12 Console 看到 v20260729a 说明加载的是最新版本;
看不到说明缓存未刷新或访问的是旧页面。
2026-07-29 19:27:52 +08:00
hjhan f5ed65aac5 fix(EQD-6838): 补网络错误/响应异常分支的约定2补充处理
calcBondForItem 的 if(!resp||!resp.obj) return 分支之前直接返回,
未调 applyBondCalcFailure,导致网络错误时另两字段和标识未清空。
现在统一清另两字段+三标识,与业务错误/开始日缺失/源字段为空一致。
2026-07-29 19:21:06 +08:00
hjhan cee69a02eb fix(EQD-6838): 修复约定2/3标识不更新的Vue响应式问题 + 组件内emit keydown + 去掉重算按钮
根因1(约定3按键不触发):
- .native 修饰符在 vue-number-input 上不可靠(jQuery在mounted中重新绑定了keydown)
- 改为在组件内部 addEventListener('keydown') + $emit('keydown')
- cshtml 从 v-on:keydown.native 改为 v-on:keydown

根因2(约定2/3标识不更新):
- bondDriverType/bondAuto/bondRev 不在后端数据中,直接赋值不触发Vue2响应式更新
- 成功分支之前用 $forceUpdate 兜底,但失败/编辑/按键分支没有
- 新增 syncBondFlags 方法,用 $set 强制触发响应式更新
- 在所有状态变更后调用:onBondPriceEdit/onBondPriceKeydown/onDpPriceInput/
  calcBondForItem成功/失败/setUnderlyingCode/加载路径

其他改动:
- 去掉"重算"按钮(用户确认不再需要)
- calcBondForItem 成功分支用 syncBondFlags 替代 $forceUpdate
- 重建 bundle.js(fastVue.base.js 是 bundle 输入)
2026-07-29 19:20:13 +08:00
hjhan d662399ab4 feat(EQD-6838): 债券三字段交互约定2/3 + 簿记逻辑1 实现
问题:
- 簿记基础逻辑1未满足:选标的后 setUnderlyingCode 调 getSpotPrice 自动填充净价/全价
- 交互约定3未满足:vue-number-input 的 input 事件只在失焦/回车时触发(__change),
  按键输入时不触发,导致用户输入过程中 源/AUTO 标识不会消失
- 交互约定2补充未满足:calcBondForItem 的早期返回分支(源字段为空/开始日缺失)
  未调 applyBondCalcFailure,导致回车后另两字段和标识未清空

修复:
- 簿记逻辑1:setUnderlyingCode 切换标的时清空净价/全价/收益率三字段,
  不再调 getSpotPrice 自动回填;refreshInitialUnderlyingPrices 续发场景也不自动填充
- 交互约定3:cshtml 三个 vue-number-input 加 v-on:keydown.native 绑定,
  新增 onBondPriceKeydown 方法——按键即清 源/AUTO 标识、标本字段 REV;
  回车键(13/108)和修饰键(Ctrl/Alt/Tab/方向键等)除外
- 交互约定2补充:calcBondForItem 的源字段为空/开始日缺失两个早期返回分支
  也调 applyBondCalcFailure,确保回车后另两字段清空、三标识全清
2026-07-29 19:09:29 +08:00
hjhan fb00f13421 fix(EQD-6838): 修复 CI 因文件名大小写和脚本 fail-fast 阻塞构建
问题:
- bundleconfig.json 中 Myjs.js/isoweek.js 在 Linux CI 上找不到
  (实际文件名是 MyJs.js/isoWeek.js,Windows 不区分大小写所以本地能跑)
- py/ps1 脚本遇到缺失输入文件时 exit 1,导致 dotnet publish 失败,
  Jenkins 构建被阻塞,部署无法进行

修复:
- bundleconfig.json: Myjs.js → MyJs.js,isoweek.js → isoWeek.js
  (与实际文件名大小写一致,Linux CI 可正确找到)
- rebuild-bundles.py/ps1: 重建模式遇到缺失输入文件时只警告不 fail,
  跳过该 bundle 保留已提交产物,避免阻塞 CI 主构建
- 只有 Verify 模式(开发本地手动校验)才因缺失/BOM 报错 exit 1

设计原则:bundle 重建是"尽力而为",绝不能阻塞主构建和部署
2026-07-29 19:01:19 +08:00
hjhan 54b8ae5cf5 [EQD-6838] bundle 重建优化:挂 Build 触发点 + CI 强制全量 + 增量检查
- csproj: GenerateBundlesBeforePublish 改为 GenerateBundlesBeforeBuild,
  挂 BeforeTargets=Build 让 IDE F5/Rebuild 也能自动重建,开发者无需记命令
- rebuild-bundles.ps1/py: 加 CI 环境检测(CI/JENKINS_HOME/BUILD_NUMBER),
  CI 下强制全量重建避免 git checkout 后 mtime 不可靠;本地开发走增量检查
- rebuild-bundles.ps1: BOM 在重建模式下只警告不 fail(重建会自动剥离),
  避免 form-layout.css 的 BOM 阻塞 CI 部署
- csproj: 新增独立 VerifyBundles target 供手动校验,不影响 Build/Publish
- csproj: 新增 SkipBundleGeneration 属性开关,特殊场景可跳过自动重建
- deploy/jenkins-build.sh: 添加 Jenkins 构建脚本参考副本(不参与 CI 执行),
  更新注释说明 target 改名后的 CI 行为
2026-07-29 18:54:38 +08:00
hjhan 76f5caabd9 fix(EQD-6604): 补 GuolianContractNoGenerator.BuildTradeNumber 纯函数,修复 UnitTestProject 编译失败
测试 GuolianContractNoGeneratorTest 调用了 BuildTradeNumber(trade, clientCode, sequenceNo) 纯函数,
但生产类此前只有依赖数据库的 Generate(YLContext, ...)。抽出不连库的 BuildTradeNumber 供测试与 Generate
共用,编号格式与现有 Generate 输出完全一致(GLMS-{client}-{yyyyMMdd}-FICC-{序号}-{去点标的})。
6 个单测全部通过,生产行为不变。
2026-07-29 18:39:09 +08:00
hjhan c912f274fd [EQD-6838] 修正前端 bundle 重建:bundle.css 历史乱码清理 + ps1 换行符统一 LF
- bundle.css: 清理 5 处历史 UTF-8 替换字符(锟斤拷)乱码注释,与重建脚本生成结果一致
- rebuild-bundles.ps1: 拼接分隔符由 CRLF 改为 LF,并归一化输入 CRLF->LF,
  使 Windows 自动生成产物与仓库内已提交(LF)制品逐字节一致
2026-07-29 17:57:01 +08:00
hjhan 2bfdce6ee0 [EQD-6838] 前端 bundle 确定性重建:自动生成脚本 + 发布期自动打 bundle
新增 rebuild-bundles.py/.sh/.ps1,按 bundleconfig.json 确定性重建 6 个 bundle 产物(剥 BOM、CRLF->LF、文件间换行分隔、CSS 相对 url() 重写,已逐字节比对验证与已提交版一致,其中 bundle.css 修正了历史打包产生的 CSS 注释 GBK 乱码)。
YLErpWeb.csproj 新增 GenerateBundlesBeforePublish 目标,在 publish 前自动运行脚本重建 bundle(CI 自动生效,无 python3 时优雅跳过回退已提交产物),从此改源文件后部署自动重新打 bundle,不再需要手工记得到处重建。
.gitattributes 将 bundle 锁为 eol=lf,.editorconfig 为 JS/TS 加 charset=utf-8,从根上防止 BOM/ZWNBSP 复发。
2026-07-29 17:47:13 +08:00
hjhan 4bebcf7fcb fix(bundle): bundle.js 纳入 ENTER 事件改动并移除 ZWNBSP;MyJs.js 剥离 BOM [EQD-6838] 2026-07-29 16:32:15 +08:00
hjhan 6911b9d758 revert: 禁用 BuildBundlerMinifier 并还原原始可用 bundle 产物,恢复测试环境
- 现象:上一提交接入 BuildBundlerMinifier 后,Jenkins 构建按 bundleconfig.json 重建的
  bundle 与原始可用版本不一致(dayjs 插件顺序错乱、且 bundleconfig.json 缺少 moment.js
  等多文件),导致 bundle.js 加载即抛 dayjs_plugin_isoWeek is not defined,整页脚本崩溃
  (jqGrid/main/otcformat 等全局未定义)。
- 本次先恢复可用性:
  1. 还原 8 个原始可用 bundle 产物(b9f2bd14 版本: jquery.js/bundle.js/vue.js/bundle.css/
     bundleV2.css/bundleV2.js/bundle.min.css/bundleV2.min.css) 重新纳入版本库,Jenkins 原样部署。
  2. 移除 YLErpWeb.csproj 的 BuildBundlerMinifier 包,避免构建时再用错乱 config 覆盖还原产物。
  3. 还原 .gitignore 中 bundle 忽略段,使产物重新被跟踪。
- 后续(待讨论):修正 bundleconfig.json 的输入顺序与文件集(对齐原始可用顺序 + 补 moment.js
  等),再重新启用自动构建,届时 fastVue.base.js 的 enter 改动会被正确打包。
- 注:本提交还原的是 enter 修复前的可用基线(页面可用,回车触发计算器仍待第二步解决)。
2026-07-29 15:57:38 +08:00
hjhan 14ed09d628 build: 接入 BuildBundlerMinifier 自动重建 bundle 产物并剥离源文件 BOM
- 根因:页面(_MainLayout 等)加载的是 bundle.js 等由 bundleconfig.json 打包的生成产物,
  但 csproj 未接入 BundlerMinifier,dotnet build 不会重生它们,Jenkins 只是把 git 里
  陈旧的 bundle.js 原样发布 -> 改了 fastVue.base.js 后回车不生效(部署的 bundle 仍是无
  enter 的旧版)。且源文件带 UTF-8 BOM,拼接后每个 BOM 落入文件中间成为 ZWNBSP,提交
  diff 充满不可见字符(历史上曾多次在产物上手工清 BOM 打补丁)。
- 修复:
  1. YLErpWeb.csproj 新增 BuildBundlerMinifier(3.2.449);其 BundleMinify 目标在
     BeforeCompile 自动按 bundleconfig.json 重建全部 bundle,Jenkins 构建即自动产出正确版本,
     不再需要手工重建并提交 bundle.js。
  2. .gitignore 忽略 8 个生成产物(jquery.js/bundle.js/vue.js/bundle.css/bundleV2.css/
     bundleV2.js/bundle.min.css/bundleV2.min.css) 并 git rm --cached 取消跟踪;这些产物
     永不再入版本库、永不陈旧。
  3. 剥离 15 个喂给 bundle 的源文件(及 css)开头的 UTF-8 BOM,使重建产物不再含 ZWNBSP。
- 验证:本地 dotnet build -t:BundleMinify 重建后 8 个 bundle 全部 BOM=0,bundle.js 含完整
  enter 链路(_enterFired/isEnter/$emit('enter'));删除 bundle.min.css 后构建可自动重建。
2026-07-29 15:42:35 +08:00
hjhan 7fc4babc28 fix(fast): 重建 bundle.js 纳入 ENTER 事件改动 + 新增 enter 链路单测
- 根因:页面(_MainLayout)加载的是 bundle.js(由 bundleconfig.json 打包 fastVue.base.js 等生成),
  而非原始 fastVue.base.js;源码树 bundle.js 是改动前的陈旧构建产物(tracked in git),
  导致测试环境部署拿到缺 _enterFired/$emit('enter') 的版本,回车不触发计算器($emit('enter') 未被发出)。
- 修复:按 bundleconfig.json 输入顺序重建 bundle.js(minify:false=顺序拼接),diff 仅 10 行,
  重建后含完整 enter 链路(_enterFired/isEnter/$emit('enter')),与源码 fastVue.base.js 一致。
- 新增 fastVue.enter.test.js(jsdom+jQuery):覆盖 numberInput 底层 Enter→_enterFired→onchange(isEnter) 与
  vueNumberInput 组件 $emit('input')+$emit('enter') 分发,9 项全过;定位期单测证明代码逻辑本身正确,
  问题在构建/部署产物陈旧。
2026-07-29 15:22:06 +08:00
hjhan b9f2bd14f6 feat(swaptrade): 债券三字段计算器交互改为回车触发并加源/AUTO/REV 三态标识
- 约定1:仅在净价/全价/收益率输入框敲回车触发计算器,失焦不触发
- 约定2:计算成功->源字段标"源"、另两字段标"AUTO";失败->保留源值、清空另两字段与标识
- 约定3:编辑某字段未回车->不联动另两字段,清源/AUTO、仅本字段标"REV"
- swapCalc.js 新增 applyBondCalcSuccess/Failure/ManualEdit 纯函数,applyBondCalcResult 第三参由 manualSet 改为 driver(跳过源字段写回)
- fastVue.base.js 新增 enter 事件区分回车/失焦,向后兼容旧的 input 行为
- TradeEdit.cshtml 三字段 input/enter 分离绑定 + 源/AUTO/REV 三态标签(去除旧铅笔图标)
- 重写 bondCalc.test.js / 更新 swapCalc.test.js 覆盖状态机;jest 68 项全过
2026-07-29 14:34:25 +08:00
hjhan 422507df8d fix(swaptrade): 新增交易切换标的时清空残留的净价/全价/收益率
setUnderlyingCode 在切换标的时仅调用 clearBondCalcFlags 清掉手动/源标志,未清字段值;getSpotPrice 只回填净价/全价,收益率(InitYtm)无刷新逻辑,导致旧债券收益率残留污染新债券。显式置空 PosiNetNoFeePrice/PosiGrossPrice/InitYtm,避免残留。
2026-07-28 18:53:57 +08:00
hjhan d0ac5c0245 fix(swaptrade): 恢复债券三字段被合并静默丢弃的徽标(源/手动编辑/重算)。修复 dotnumber 合并(32af85d3)三路冲突按 theirs 解决时无冲突标记却静默丢弃重算链接/源徽标/手动铅笔图标;JS 逻辑本就完好,仅界面可视化指示回归。 2026-07-27 15:16:12 +08:00
hjhan e188ba5038 test(swap): 新增 ClosePercent 口径A产品契约测试,防止平仓比例被改为占剩余口径
新增前后端回归测试,锁定产品需求:ClosePercent 永远是占期初名义本金的比例。后端 CPC_001~010 装配测试验证 SwapDealService 真实执行 ToRemainingClosePercent/ToOriginalClosePercent 转换;前端 16 项接线测试验证 unwindSwapTrade.js 使用 NotionalValue 作为分母且 postData 携带 notionalValue 字段。测试背景:c9071a4e 曾将 ClosePercent 改为占剩余口径导致回归但现有测试全通过,本测试通过源码装配与契约验证弥补该缺口。
2026-07-27 13:18:32 +08:00
hjhan e1e65929f1 债券互换三字段互算:手动编辑标识由文字改为铅笔图标+hover说明
- 三列(DP/CP/YD)的橙色"手动"文字 span 改为 glyphicon-pencil 图标,
  配 title="手动编辑:该字段由您填写,不会被计算器反算覆盖",
  对应需求3的"单独编辑"语义,hover 即可见说明,比纯文字更直观。
- 复用项目既有 Bootstrap Glyphicons + title 原生 tooltip 约定,零新依赖。
- 顺带修复 451 行"发行信息"图标重复 class(glyphicon glyphicon glyphicon-edit→glyphicon glyphicon-edit)笔误。
2026-07-23 16:53:07 +08:00
hjhan 8694e6e3f4 债券互换三字段互算:估值日(开始日)缺失报错 + 补提漏交回归用例
- 估值日严格取互换起始日 StartDate,去掉"悄悄回退交易日(TradeDate)":
  真的为空时返回错误提示("请先填写开始日…或手动填写三项数值")并 return,
  不调用计算器、不覆盖手工输入,与需求(按开始日估值)完全一致。
- 抽纯函数 getBondStartDateMissingMsg(StartDate 空→文案/null) + 导出;
  复用 D3 的 shouldShowBondErr 去重,避免空开始日时价格格逐键连刷。
- BondCalcHepler.cs 仅补 XML 注释说明前端已强制 StartDate 必填;
  targetDate 空→代理默认 T+1 行为不变(兜底 RealtimePnlCalc 旧链路)。
- 补提此前漏提交的回归用例:bondCalc.test.js(×100/÷100 端到端 + 逐字段手动锁定)、
  swapCalc.test.js(D1/D2/D3 + 本次估值日缺失守卫)。
- jest bondCalc+swapCalc 全绿(68 passed)。
2026-07-23 16:49:40 +08:00
hjhan 8b8fee9306 债券互换三字段互算:修复单位换算与重开覆盖/提示噪声(D1/D2/D3)
- 发计算器前×100、回写÷100(bondPriceToCalc/bondCalcPriceToStorage),
  根治 -117.93/378543 离谱值(存储态小数↔展示态百分比对齐)
- D1:setUnderlyingCode 切债券清空 bondManual/bondDriverType,
  避免旧债券手填态污染新债券
- D2:重开(刷新/审批重开)已保存债券单时,加载路径对三字段齐全的
  标的锁为手动态,编辑某格不联动另两格(手动覆盖跨会话 sticky)
- D3:不可算债券逐键手填时,同一失败提示只弹一次,抑制 toast 连刷
- fe-tests:+9 用例覆盖 D1/D2/D3 边界(切标的清标志/重开锁定/失败提示去重)

jest bondCalc+swapCalc 全绿(62 passed)
2026-07-23 16:37:13 +08:00
hjhan 185a23e978 fix(swap-trade): 修复新增互换交易时 InitYtm 为 null 触发数据库约束报错
根因:InnerSaveNewTrade 只做 req.Clone() 直接入库,未给 trade.InitYtm 赋值;
而编辑场景(InnerSaveEditTrade:582)有从 swap_positions 取 InitYtm 的逻辑,
新增场景漏了。前端填的"成交收益率"存在 swap_position.InitYtm 上,但 trade
表 InitYtm 列要求 NOT NULL,新增时为 null 直接报错。

修复:新增场景同样从 req.swap_positions 取 InitYtm,无则默认 0(与
PrepareTrade:353 的 flowMerge.InitYtm ?? 0 处理一致)。

验证:dotnet build 0 错误
2026-07-23 13:52:04 +08:00
hjhan d0c170c2a1 fix(log): 修复 DbUpdateException 日志丢失 InnerException 的问题
- NLog.config: exception layout 从 format=Message 改为 format=toString
  原配置只渲染外层异常 Message,不递归 InnerException。DbUpdateException
  外层 Message 永远是 "See the inner exception for details.",真正的 SQL
  错误(主键冲突/字段超长/外键约束)全在 InnerException 里被吞掉。
  改为 toString 后输出完整异常链(含 InnerException + 堆栈),全局生效。
- SwapTrade2Controller.tradeEditJson catch 块对齐其他 Controller:
  e.Message → e.GetBaseException().Message,并补上 e.ToJson() 数据参数,
  让前端也能拿到最内层错误信息。

验证:dotnet build 0 错误
2026-07-23 13:43:43 +08:00
hjhan d768a83387 test(swap-calc): 补充债券计算器单位换算端到端测试 + 精化错误提示文案
- bondCalc.test.js 新增 3 个测试:bondPriceToCalc/bondCalcPriceToStorage
  单位换算边界 + 复刻 -117.93/378543 离谱值根因验证
- swapCalc.js 注释从"主数据量纲错误"改为"前端↔计算器单位换算不匹配",
  错误提示文案优化(无逻辑变更)
- 26 个测试全通过
2026-07-23 11:35:27 +08:00
hjhan 7b007bddfa refactor(swap-test): 抽取 TestableSwapEodPositionService 公共基类收敛重复 Stub
- 新建 TestableSwapEodPositionService 收敛 8/8 Stub 重复的高频 override
  (PersistEodSwapPosition/SaveAllChanges/GetCurrencyRate/AddClientCash)
  + 统一 OptUserInfo 构造 + PersistedPositions/SaveChangesCount 输出捕获
- SwapEodPositionService.DealInterests 改 protected virtual(行为零变化)
- 8 个 ScenarioTest 改为继承基类,删除重复 override
- 消灭 DealInterestsScenarioTest/DealInterestsGoldenReplayTest 的反射调用
  (typeof().GetMethod().Invoke → 直接调用 DealInterests)

验证:dotnet build 0 错误;dotnet test SwapModule 284通过/6跳过/0失败
2026-07-23 11:16:11 +08:00
hjhan 63d9decd17 恢复对债券首次读取接口返回的 price / netPrice 本来就是存储态小数 (EodPrice 直接返回 DB 值;非 EodPrice 分支 ×bondPriceMultiple=0.01 2026-07-22 15:04:42 +08:00
hjhan e331c15a15 计算器 结果还是会被 不断 /100 2026-07-22 14:48:52 +08:00
hjhan 3cf06c2be7 债券计算器 返回结果后要进行 /100 * 100 处理 适配目前的 这个 /100 * 100,避免 很离谱的 值 2026-07-22 14:00:55 +08:00
hjhan 1373f57884 feat(swap): 债券互算接入浮动收益端(期初三字段change触发+手动/源徽标+重算)
- swapTradeEdit.onBondPriceInput:编辑净价/全价/收益率之一即标记该字段为"已手动设过"(bondManual[type]=true) 并以之为源调 /Bond/CalcBond,反算仅填充未手填字段。

- calcBondForItem:估值日用整笔交易 trade.StartDate(回退 TradeDate→null/T+1);用 main.post(...,{alertFn:main.message}) 让业务/参数错误以非阻塞 toast 展示、绝不回写;未选债券标的先提示;回写前用 getBondCalcErrorMessage 判错。

- resetBondCalc:清空手动锁定、以当前某字段为源重新自动反算。

- setUnderlyingCode:标记浮动腿 isBond(债券标的) 以启用互算。

- TradeEdit.cshtml:三字段加 v-on:change 触发 + 绿色"源"/橙色"手动"徽标 + "重算"链接。
2026-07-22 11:36:00 +08:00
hjhan d3b47f6fa1 feat(swap): 债券净价/全价/收益率互算核心(逐字段手动锁定+错误不覆盖纯函数+测试)
- swapCalc.applyBondCalcResult(state, calc, manualSet):回写时跳过"用户手填过的字段"(逐字段手动锁定,永不覆盖),只反向填充未手填字段;EPS 去抖避免光标跳动。

- 纯函数 getBondCalcErrorMessage(resp):空响应/errCode!=0 返回文案、否则 null,与 C# errCode 守卫一一对应,保证"坏结果"不静默回写。

- 新增 jest 用例覆盖:三字段互算、逐字段锁定(手填全部三个无一被回写)、业务错误不覆盖手工输入、getBondCalcErrorMessage。85→86 全绿。
2026-07-22 11:35:53 +08:00
hjhan 0aad4c8040 feat(swap): 债券计算器C#契约对齐——targetDate估值日透传·错误处理透传真实原因·链路日志
- BondCalcHepler.BondCalc 新增可选 targetDate(估值日) 参数并写入请求体,透传至代理 zszq-bond-oms 的 targetDate(→bond-calc settlementDate);不传则沿用代理默认 T+1,与 bond-oms-ui 契约一致。

- 新增 out errorMsg 重载:地址未配/无响应/success=false/业务错误码 errCode!=0/异常 均返回 null 并透出真实中文原因;日志级别由 Info 升为 Error,try/catch 包裹。

- BondController.CalcBond 把 errorMsg 透传给 JsonError,不再硬编码"计算价格失败"。

- BondCalcByDate(自动结算路径) 同步加 errCode/空守卫,避免回写坏数据。
2026-07-22 11:35:41 +08:00
hjhan e7c6e432e1 test: 修复陈旧 golden 与恢复 Step1 Ignore
golden_互换结清后待实现归零.json 的 RealizedPnl 0->8.219(f4ffe710 修复后未重录,8.219为正确值); SwapReEodDeleteManualCashRecordTest.Step1 恢复 [Ignore](与类头契约一致,避免污染入库 golden)。
2026-07-21 15:28:17 +08:00
hjhan 985162242d feat(greeks): 新增 risk-factor bump 计算与注册
ParameterBase.Clone() 保留运行时类型深拷贝; ValueCalculator 两个薄接入方法; GreeksBumpCalculator/GreeksRiskFactor 引擎。加法性重定价桥,不动现有定价输出。
2026-07-21 15:28:15 +08:00
hjhan a179675efd refactor(swap-deal): 前端盈亏校验抽为 SwapFrontendPnlValidator
BuildFrontendValidationDiffs/FrontendPnlDiff/AddDiffIfOverThreshold 从2043行 SwapDealService 移至新文件,本文件仅留调用+日志。纯位置迁移,零行为变更。
2026-07-21 15:28:14 +08:00
hjhan a153a1d1a6 refactor(swap-eod): 抽出期末头寸纯函数并放宽可见性
SetFixedLegRealizedPnl 替换4处复制粘贴(清理L1296双分号); NormalizeInterestSignForReport 替换16行内联报表分支; CalculateWeightedMarginInterest private->public static。均为纯静态无实例依赖,配套无库单测锁定行为。
2026-07-21 15:28:13 +08:00
hjhan 494a7a06cc test(swap): 修正平仓利息复现用例标的代码为 2500002.IB
- 将 GLMS20260703CloseInterestTest 中 UnderlyingCode 由 511160.SH 更正为真实标的 2500002.IB
- 核心复现用例(复利真实FR007→-13667.85 / 单利→-13668.02)保留, 作为代码级验算样例
2026-07-20 18:41:16 +08:00
hjhan 98bfe8fb5d test(swap): 增加 GLMS-20260703-0002 平仓利息复现用例(真实FR007+真实函数)
- 调用系统 GetInterests 真实路径(复利/单利两条), 用截图中的真实 FR007 定盘值
  在 07-06/07-13 两个重置点真实取数, 复现系统值 -13,667.85 与业务实际值 -13,668.02
- 断言两者差异 0.17 元来自复利第二段本金并入
- 仅保留调用系统函数的断言, 不内嵌手算, 验算见 GLMS20260703_平仓利息验算.xlsx
2026-07-20 18:31:19 +08:00
hjhan 42602055de test(eod): 为 FR007 入库 UnderlyingId 守卫补纯函数单测
- EodPriceService 抽出纯函数 ResolveUnderlyingIdForCode(行为不变),SyncUnderlyingIdFromCode 调用之,便于无库单测
- 新增 EodPriceUnderlyingIdGuardTest:7 用例全过,覆盖空代码/标的不存在维持原值、已一致不改动、FR007 错行(511160.SH=2173889、159111.SZ=2173890)校正为 2170838、非错配不互相覆盖
- 对应根因:GLMS-20260701 FR007 价格行 UnderlyingId 错挂导致网页查得到/结算查不到
2026-07-17 11:24:11 +08:00
hjhan e261137c1d fix(swap): 修复多次部分平仓 ClosePercent 口径(A占期初→B占剩余)转换缺失
- SwapDealService.ApplySwapTrade 入口补 ToRemainingClosePercent(A→B) 转换,与 SwapUnwind 保持一致;缺此转换导致 SaveSwapDealInternal 的 B→A 还原出错(GLMS-20260701-0006:第二次部分平仓50%被错误还原为32.5%)
- unwindSwapTrade.js 改用 swapCalc.calcCloseQtyByOriginalPercent 的 roundHalfAwayFromZero,规避 JS 浮点精度偏差(32500000*(0.5/0.65) 应为 25000000)
- SwapUnwind.cshtml 引入 swapCalc.js
- 新增 ApplySwapTradeClosePercentBugTest 回归测试;补充 swapCalc.test.js 精度用例与 GLMS20260701DbDiagnoseTest 诊断测试([Ignore])
2026-07-17 10:59:18 +08:00
hjhan 75988a33eb fix: eod_commodity_future_price 入库前以 UnderlyingCode 重派生 UnderlyingId,防止 FR007 等价格行 UnderlyingId 与 FutureContractId 失同步
- 新增 EodPriceService.SyncUnderlyingIdFromCode:入库前以 UnderlyingCode(FutureContractId) 为准反查真实 UnderlyingId,不一致则告警并自动校正
- SaveEodFuturePrice(web编辑/新增)、SettlementPriceImportService(xlsx上传)、EodSyntheticPriceSaveService(系统合成价) 写入前调用
- 根治'网页端能查到、EOD结算查不到'的失同步问题
2026-07-17 10:32:57 +08:00
hjhan dddf0782bd Merge branch 'origin/feature/p132_74-risk-engine' into glms/feature/1.4.2 (规则批量启用/应用审批优化) 2026-07-17 09:57:08 +08:00
hjhan 21b86c871f test(trade): 补 23108016 互换/期权本次名义本金分支单元测试
抽 CalcSwapCloseNotionalFromEventData 和 CalcOptionCloseNotional 为 public static 纯函数, BuildTriggerContext 了结场景改为调用这两个函数. 新增 13 个单元测试覆盖: 互换分支 EventData null/空/非法JSON/正数/负数/零 容错与绝对值, 期权分支 两者null/单边null/正数相乘/负数容错 绝对值语义. 重点锁定静默 catch 容错契约, 防止 EventData 异常时名义本金变 0 绕过审批阈值.
2026-07-17 08:45:49 +08:00
hjhan 2395aa9856 test(swap): 补 6676b625 掉期保证金利息方向反向单元测试
抽 CalculateSwapRealizedPnl 为 public static 纯函数(用 ConsTrade.InterestMarginModels 替代实例 marginTypes), 新增 8 个单元测试覆盖: 非保证金腿收取/支付原向, 保证金腿(初始预付金/追加预付金)收取/支付反向, 完整 5 字段汇总两种方向, RealizedInterest=0 边界. 锁定 6676b625 修复的保证金腿利息方向反向契约, 防止后续误改回归.
2026-07-17 08:40:19 +08:00
hjhan 96a64ae82d test(swap): 修复 GLMS20260701DbDiagnoseTest 编译错误并加 2.3c 模拟 API 流程
L208-209 Convert.ToDecimal 修复 double?  decimal 隐式转换错误(CS0266)和非 nullable double ?? 0 错误(CS0019). 新增 2.3b 直接调用 ResolveInterestLegPositions 验证 Clone 行为, 2.3c 模拟 controller 完整流程(ToRemainingClosePercent + GetUnwindInterests) 等价于 HTTP API. 该测试带 [TestCategory(DbDiagnose)] 标记不进 CI, 作为可重复手动诊断工具保留.
2026-07-17 08:24:00 +08:00
hjhan 43679393bb test(swap): 补 e2fb456b InitUnwind 默认 ClosePercent 单元测试
抽 CalcDefaultInitClosePercent 静态纯函数(PosiNotionalValue/NotionalValue), InitUnwind L270 改为调用此函数, 新增 6 个单元测试覆盖: 未平仓/已平30%/除零保护/GLMS-20260701-0013 真实快照/与 ToRemainingClosePercent 联动自洽不变式/全平完边界场景. 填补 e2fb456b 提交的测试覆盖缺口.
2026-07-17 08:23:50 +08:00
hjhan 62c26bd7f5 fix 部分平仓 选择切换 会导致不一致,浮动端会错误 2026-07-16 20:57:18 +08:00
hjhan e2fb456bc3 fix 平仓(InitUnwind L267)硬编码 1而不是剩余平仓比例 2026-07-16 20:07:50 +08:00
hjhan 238824f4f4 fix(预付金返回/平仓比例): 恢复占期初语义并在后端入口统一转占剩余
之前提交 4f46e263 误将平仓比例改为占剩余持仓(Definition B)语义,与需求不符。
正确需求:每次打开平仓/提前终止均基于最开始原始名义本金(Definition A),
但每次返回的预付金金额需正确(后端按占剩余计算)。

- SwapDealService: 新增 ToRemainingClosePercent/ToOriginalClosePercent 双语义转换;
  SwapUnwind 入口把前端传来的占期初(A)转为占剩余(B),全计算链公式不动;
  SaveSwapDealInternal 序列化前还原为 A 落库,事件列表读取即显示占期初比例。
- SwapTrade2Controller.GetUnwindInterestList: 接收期初/剩余名义本金,预览利息按 B 计算。
- unwindSwapTrade.js: 回退为占期初语义(ClosePercent/平仓名义本金用 NotionalValue,
  oriClosePercent=剩余/期初),提前终止同理。
- 单测 UW_007/008 覆盖 A→B 全平/部分平仓判定。

不影响收盘后盯市/估值(仅平仓入口与展示口径)。
2026-07-16 19:54:26 +08:00
hjhan 6424b38001 fix(期权到期): checkEodPrice 前置校验改用债券感知统一取价方法
- trade_cashController.checkEodPrice 是前端'执行到期'按钮的前置 gate,
  原用 EodPriceQueryService.TryGetEodPrice(仅查期货+股票两表),
  债券标的(如 GLMS-20260715-0002 / 180011.IB)必然取不到价而报
  '交易日{date}的结算价或收盘价未找到',挡在 tradeExpireConfirm 之前,
  导致层1 对 tradeExpireInner/MultipleTradeExpireConfirm 的修复被绕过。
- 改为与层1 一致的 EodPriceQueryService.TryGetSettlementEodPrice
  (债券走中债估值、期货/股票走原路径),三处到期取价口径统一。
2026-07-16 14:56:28 +08:00
hjhan a17cdc2b74 fix(期权到期): 债券标的到期取价补查中债估值表,并标注债券净/全价映射不一致(Layer2)
- EodPriceQueryService 新增 TryGetSettlementEodPrice:债券走中债估值、期货/股票走原路径
- 单笔 tradeExpireInner + 批量 MultipleTradeExpireConfirm 改用统一方法,修 GLMS-20260715-0002 债券期权到期报'结算价未找到'
- 移除批量路径未初始化的 EodPriceProvider(对债券无效且有误导性的 footgun)
- Layer2:标注 EodPriceProvider.Initialize 与 GetBondPrice 债券 ClosePrice/SettlePrice 净全价定义相反,待统一(不改逻辑)
- 新增白盒单测覆盖债券标的到期取价(3用例 DB驱动,均通过)
2026-07-16 14:41:44 +08:00
hjhan 3c9d22f60d test(swap): 浮动腿 fpositions 用 orig 不影响预付金结果 — 诊断测试
验证 GLMS-20260701-0008 的预付金 bug 修复无需触碰浮动腿(fpositions / posiLong/posiShort):

- 本 deal 利息腿仅 mode 9(标的期初全价) + mode 5(初始预付金),无「多头/空头存续名义本金」腿。
- CalcNotionalByMode 中 posiLong/posiShort 仅在「多头/空头存续名义本金」分支被消费(L709-716),
  mode 9/5 均不依赖之;故 fpositions 用 orig 100M 不影响预付金「返回预付金」计算。
- 诊断用例用反射调 private CalcNotionalByMode 实证:mode9 closePrincipal=posiNotional×比例、
  mode5 closePrincipal=实时腿 Fix×比例,均与 posiLong(orig 100M) 无关;并证明若存在
  多头存续名义本金腿则 orig 会算错(10M vs 正确 6.89M)——普通收益互换的潜在同类 bug,本 deal 不触发,属修复范围外。

不改动任何生产代码。3 个诊断用例全过。
2026-07-16 13:00:21 +08:00
hjhan 3654841b2e merge: 合并 feature/p132_74-risk-engine 最新改动到 glms/feature/1.4.2
合并风控引擎分支最新提交(风控超时校验SQL、标的/合约类型传参、
UnderlyingAssetClass 字段、缓存刷新与异步逻辑优化等),无冲突。
2026-07-16 11:14:43 +08:00
hjhan d51dd8a60b test(swap): 回归测试对齐 GLMS-20260701-0008 四次部分平仓快照
生产 deal 又平仓两次(共四次),实时预付金腿 InterestPrincipalFix 由 73,260
降至 66,813.12(4 次返还 9,900+15,840+3,663+2,783.88=32,186.88;
99,000−32,186.88=66,813.12,与 dev 库实时腿完全勾稽)。

- SwapUnwindPrepayOrigVsRealBugTdd:RemainingFix 常量 73,260→66,813.12;
  头部注释同步为四次平仓;新增 GLMS20260701_四次部分平仓_LiveSnapshot 用例,
  把 4 次平仓真实数据硬编码为忠实回归(日后该 deal 再平仓需同步更新)。
- 纳入此前遗漏提交的连库诊断测试 GLMS20260701DbDiagnoseTest.cs
  (使用 DbContextFactory 取连接串,无硬编码密码,安全)。

验证:dotnet test --filter SwapUnwindPrepay → 26 passed(19+7)失败 0;
YLErpDAL / UnitTestProject 编译通过(仅既有警告)。
2026-07-16 11:09:47 +08:00
hjhan 24266cfad4 fix(swap): 多次部分平仓预付金返还默认显示初始值(GLMS-20260701-0008)
根因: GetUnwindInterests 利息腿迭代源取 origPositions(IsInitial=1),
其预付金腿 InterestPrincipalFix 恒为初始值(99000); 而"当前剩余本金"(73260)
存于实时腿 realPositions(IsInitial=0)。CalcNotionalByMode 与 InitSwapDealInterest
均读 position.InterestPrincipalFix, 导致多次部分平仓后平仓页默认按初始本金计算
返还。首次平仓 orig==real 掩盖了 bug, 故此前"只修好一次部分平仓"。

修复: 抽纯函数 ResolveInterestLegPositions(orig, real)——迭代源仍用 origPositions
(保留 orig.id -> eod_swap_position.PositionId 的日终匹配, 全库 eod 均按 orig.id 归档,
换 realPositions 会破坏 preEod 匹配导致利息重算错误), 仅对预付金腿(初始5/追加6)
Clone 覆盖 InterestPrincipalFix 为实时腿剩余本金; real.PositionId == orig.id 精确
1:1 关联; orig==real 时返回原始腿本身不克隆。

测试: 新增 SwapUnwindPrepayOrigVsRealBugTdd(6 用例)直接单测该纯函数, 覆盖既有
19 个用例(均绕过 orig/real 选择)的盲区。dotnet test --filter SwapUnwindPrepay
25 passed(19+6) 失败 0。
2026-07-16 10:49:12 +08:00
hjhan 4f46e2634f 修复收益互换多次部分平仓预付金返还:平仓比例/名义本金改以剩余持仓为基准
unwindSwapTrade.js:
- oriClosePercent 恒为 1(Definition B 下最多平 100% 剩余,修正全部平仓/按比例少返预付金 + 进页面 getInterestList 少算)
- CloseMethod==1(全部平仓)时修正 ClosePercent=1;CloseMethod==2(部分平仓/待复核)保留已提交比例,避免覆盖
- changeCloseQty/Percent/NotionalValue 三处换算改用 PosiNotionalValue(剩余本金),不再用原始 NotionalValue

swapCalc.js: 新增 calcCloseNotionalByRemaining / calcClosePercentByRemaining 纯函数(含除零保护)
swapCalc.test.js + _shim_run.js: 补充多次部分平仓换算单测(剩余vs原始对比、第3次全平、3次合计=原始、除零保护)
2026-07-16 10:03:21 +08:00
hjhan d69faaa8e8 fix(fast): 修复 numberInput 粘贴路径 percent 字段 ×100,并补 Jest 粘贴回归单测
- fastVue.base.js: __onInput 粘贴分支原把显示值当模型值传入 setValue 导致 percent 字段 ×100;
  改为先按 __change 同款口径(去千分位逗号 + 按 percent/append:'%'/append:'‱' 除法)解析为模型值再回显,
  使 DOM/显示/模型三者一致;键盘输入路径不变
- 重新生成 Statics/bundles/bundle.js:去每个输入文件头的 UTF-8 BOM(避免 bundle 中间出现 ZWN BSP 不可见字符),
  页面实际加载 bundle.js(改源文件必须重生成 bundle 才生效)
- 新增 numberInput.paste.test.js 覆盖 percent:true/append:%/append:‱/普通数字 4 类字段的 Ctrl+V 粘贴(Jest+jsdom,4 passed)
- fe-tests package.json 增加 jest-environment-jsdom、jquery 依赖
2026-07-15 12:58:08 +08:00
hjhan 94827eb6b6 fix(fast): 修复 numberInput 粘贴路径 percent 字段 ×100,并补 Jest 粘贴回归单测
- fastVue.base.js: __onInput 粘贴分支原把显示值当模型值传入 setValue 导致 percent 字段 ×100;
  改为先按 __change 同款口径(去千分位逗号 + 按 percent/append:'%'/append:'‱' 除法)解析为模型值再回显,
  使 DOM/显示/模型三者一致;键盘输入路径不变
- 重新生成 Statics/bundles/*.js:去每个输入文件头的 UTF-8 BOM(避免 bundle 中间出现 ZWN BSP 不可见字符),
  页面实际加载 bundle.js(改源文件必须重生成 bundle 才生效)
- 新增 numberInput.paste.test.js 覆盖 percent:true/append:%/append:‱/普通数字 4 类字段的 Ctrl+V 粘贴(Jest+jsdom,4 passed)
- fe-tests package.json 增加 jest-environment-jsdom、jquery 依赖
2026-07-15 11:29:14 +08:00
hjhan 05826fe7d5 debug(fast): 为 numberInput 粘贴路径加 F12 诊断日志并清理 bundle.js 中的 BOM
- fastVue.base.js: 在 __onInput 粘贴分支和 __change 加入 console.log,
  输出 raw/parsed/append/percent,便于在浏览器 F12 排查粘贴值实际走向
- 重新生成 bundle.js,并去掉每个输入文件头的 BOM,避免 bundle.js 中间出现 ZWN BSP 不可见字符
2026-07-15 11:08:25 +08:00
hjhan 2a5e338b50 fix(fast): 修复 numberInput 粘贴路径 percent 字段 ×100,并补 Jest 粘贴回归单测
- fastVue.base.js: __onInput 粘贴分支原把显示值当模型值传入 setValue 导致 percent 字段 ×100;
  改为按 __change 同款口径先解析为模型值再回显,键盘输入路径不变
- 重新生成 Statics/bundles/bundle.js(fastVue.base.js 被合并进此 bundle,页面实际加载该文件,
  改源文件不生效必须重生成 bundle)
- 新增 numberInput.paste.test.js 覆盖 percent:true/append:%/append:‱/普通数字 4 类字段的 Ctrl+V 粘贴
- fe-tests package.json 增加 jest-environment-jsdom、jquery 依赖
2026-07-15 11:08:25 +08:00
hjhan ea313dc1c6 fix(fast): 修复 numberInput 粘贴路径 percent 字段 ×100,并补 Jest 粘贴回归单测
- fastVue.base.js: __onInput 粘贴分支原把显示值当模型值传入 setValue 导致 percent 字段 ×100;
  改为按 __change 同款口径先解析为模型值再回显,键盘输入路径不变
- 新增 numberInput.paste.test.js 覆盖 percent:true/append:%/append:‱/普通数字 4 类字段的 Ctrl+V 粘贴
- fe-tests package.json 增加 jest-environment-jsdom、jquery 依赖
2026-07-15 10:18:26 +08:00