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 例全过
This commit is contained in:
hjhan
2026-07-30 10:15:00 +08:00
parent ead2c01f0d
commit 72b2a4cbf6
2 changed files with 25 additions and 0 deletions
+12
View File
@@ -135,6 +135,18 @@ describe('diag: 源码守卫(防回归——不许再硬编码易过期版本
expect(src).toMatch(/otcDebug\.banner|window\.ylotc\.__diag/);
});
test('swapTradeEdit.js 三字段调试埋点必须 gated(otcDebug.log),不得裸 console.log 退回', () => {
// 防回归:历史多次"改了不生效"靠日志兜底,曾出现①把 console.log 直接删了(出问题时看不见)
// ②或解除了开关控制(生产噪音)。正确做法是 otcDebug.log?otcdebug=1 才输出)。
const src = fs.readFileSync(path.join(SCRIPTS, 'app/swaptrade/swapTradeEdit.js'), 'utf8');
// 存在 gated 埋点
expect(src).toMatch(/otcDebug\.log\(/);
// 关键路径不得出现裸 console.log 调试行
expect(src).not.toMatch(/console\.log\('\[onBondPriceKeydown\]/);
expect(src).not.toMatch(/console\.log\('\[calcBondForItem\]/);
expect(src).not.toMatch(/console\.log\('\[onBondPriceEdit\]/);
});
test('fastVue.base.js 顶部 banner 不再硬编码 v20260729a', () => {
const src = fs.readFileSync(path.join(SCRIPTS, 'fast/fastVue.base.js'), 'utf8');
expect(src).not.toContain('v20260729a');
@@ -352,6 +352,7 @@ const vue = new Vue({
if (!item.isBond) return;
SwapCalc.applyBondManualEdit(item, type);
this.syncBondFlags(item);
otcDebug.log('[onBondPriceEdit] type=' + type + ' after: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
},
//交互约定3(按键实时触发):vue-number-input 的 input 事件只在失焦/回车时触发,
// 按键输入过程中不触发,导致用户输入时 源/AUTO 标识不会消失。
@@ -360,13 +361,19 @@ const vue = new Vue({
// 修饰键(Shift/Ctrl/Alt/Tab/方向键/F键等)除外——这些不改变数值,不应清标识。
onBondPriceKeydown(item, type, event) {
if (!item.isBond) return;
var kc = event.which || event.keyCode;
// 仅"会改变数值"的按键才清 源/AUTO 标识并标本字段 REV;
// 回车/修饰键/导航键/功能键/纯 Shift 不改标识(见 SwapCalc.isBondPriceValueKey)。
// 修复:此前守卫漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F键/Shift(16)
// 导致"鼠标放进去没改值、只按了方向键/功能键"也会误标 REV。
if (!SwapCalc.isBondPriceValueKey(event)) return;
// 调试埋点(otcDebug 仅在 ?otcdebug=1 / localStorage.otcdebug=1 时输出,平时零噪音):
// 抓"逐键前后 driver/rev 变化",用于定位 REV 标记异常。
otcDebug.log('[onBondPriceKeydown] type=' + type + ' kc=' + kc +
' before: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
SwapCalc.applyBondManualEdit(item, type);
this.syncBondFlags(item);
otcDebug.log('[onBondPriceKeydown] after: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
},
//确保 bondDriverType/bondAuto/bondRev 的变更触发 Vue 响应式更新。
//swapCalc.js 的纯函数直接对 state 赋值,但这些属性不在后端数据中(非响应式),
@@ -433,6 +440,9 @@ const vue = new Vue({
// 该字段在页面上本就是可选择的日期控件(vue-datepicker),用户可改;现已默认即有。
// 不再"悄悄回退到交易日(TradeDate)"——真的为空时直接报错提示,由用户填写/手动填三数(需求2)。
var targetDate = (this.trade && this.trade.StartDate) ? this.trade.StartDate : null;
// 调试埋点(otcDebug 仅在 ?otcdebug=1 时输出):抓"回车→调计算器→源/结果"全链路,定位三字段互算异常。
otcDebug.log('[calcBondForItem] enter driver=' + driver + ' priceType=' + priceType +
' price(sent)=' + SwapCalc.bondPriceToCalc(price) + ' targetDate=' + targetDate);
var sdMsg = SwapCalc.getBondStartDateMissingMsg(targetDate);
if (sdMsg) {
// D3 同款去重:开始日缺失时用户在价格格逐键输入会连刷相同提示,故同一文案只弹一次
@@ -466,6 +476,7 @@ const vue = new Vue({
if (SwapCalc.shouldShowBondErr(item, err)) main.message(err);
SwapCalc.applyBondCalcFailure(item, driver); // 交互约定2:失败→清另两字段+标识
self.syncBondFlags(item);
otcDebug.log('[calcBondForItem] calc-error driver=' + driver + ' msg=' + err);
return;
}
item._lastBondErr = null; // 成功则清标记,便于下次真出不同错误时仍能提示
@@ -487,6 +498,7 @@ const vue = new Vue({
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
SwapCalc.applyBondCalcSuccess(item, driver); // 交互约定2:成功→标 源/AUTO
self.syncBondFlags(item);
otcDebug.log('[calcBondForItem] success driver=' + driver + ' auto=' + JSON.stringify(item.bondAuto) + ' rev=' + JSON.stringify(item.bondRev));
// 名义本金依赖全价(PosiGrossPrice):以净价/收益率为源反算出的全价被回写后,
// 直接赋值不会触发组件 input 事件,需在此显式重算,保持名义本金与全价一致。
if (self.calcNotional) self.calcNotional();
@@ -501,6 +513,7 @@ const vue = new Vue({
// (与 .done 内 !resp.obj 分支行为一致;左侧 .done 的 getBondCalcErrorMessage 分支为兼容冗余,主路径在此)。
SwapCalc.applyBondCalcFailure(item, driver);
self.syncBondFlags(item);
otcDebug.log('[calcBondForItem] post-fail/reject driver=' + driver);
});
},
//变更收费基本单位