feat(eodswapposition): 新增框架合约新口径展示功能

- 在EodPnlCalculator中添加CalculateEodSwapRiskNewFields方法,实现新口径计算逻辑
- 创建EodSwapRiskNewFields和EodSwapRiskNewResponse模型类,支持新的展示字段
- 实现SearchEodSwapNewList查询方法,复用旧查询权限控制并计算新字段
- 添加index=3的新框架合约Tab页面,使用独立查询接口和列配置
- 前端JavaScript中实现colModelGridEodSwapNew列模型,替换旧字段并新增9个字段
- 完善导出功能支持新Tab的标准格式导出,保持与旧口径分离
- 更新测试用例验证新框架合约前端接线正确性
This commit is contained in:
张名锐
2026-08-25 13:49:30 +08:00
parent d43e77ee6f
commit 7d0eb16f64
8 changed files with 655 additions and 10 deletions
+66 -3
View File
@@ -4,7 +4,8 @@ const vm = require('vm');
function loadEodPositionRiskHelpers() {
const filePath = path.join(__dirname, '../wwwroot/Scripts/app/swaptrade/EodPositionRisks.js');
const code = fs.readFileSync(filePath, 'utf8') + '\nmodule.exports = { TradeDirectionFormat };';
const source = fs.readFileSync(filePath, 'utf8');
const code = source + '\nmodule.exports = { TradeDirectionFormat, colModelGridEodSwap, colModelGridEodSwapNew, eodSwapGroupConfig, eodSwapRiskNewGroupConfig };';
const sandbox = {
module: { exports: {} },
exports: {},
@@ -13,14 +14,25 @@ function loadEodPositionRiskHelpers() {
numberFormat() {
return function () { };
}
},
otcformat: {
trading: {
notional() { return ''; },
StockEqvNotional() { return ''; },
tradePrice() { return ''; }
}
},
swapPricePrecision: {
format() { return ''; }
}
};
vm.runInNewContext(code, sandbox, { filename: filePath });
return sandbox.module.exports;
return { helpers: sandbox.module.exports, source };
}
const { TradeDirectionFormat } = loadEodPositionRiskHelpers();
const loaded = loadEodPositionRiskHelpers();
const { TradeDirectionFormat, colModelGridEodSwap, colModelGridEodSwapNew, eodSwapGroupConfig, eodSwapRiskNewGroupConfig } = loaded.helpers;
describe('互换日终持仓交易方向', () => {
test.each([
@@ -37,3 +49,54 @@ describe('互换日终持仓交易方向', () => {
expect(TradeDirectionFormat(1, {}, { eodPosition: { PosiDirection: 0, PositionType: 1 } })).toBe('');
});
});
describe('EQD-7084 新框架合约前端接线', () => {
test('新列模型保留旧列并追加九个字段,六个展示列绑定 NewFields', () => {
const oldColumns = colModelGridEodSwap();
const newColumns = colModelGridEodSwapNew();
const oldNames = oldColumns.map(column => column.name);
const newNames = newColumns.map(column => column.name);
const replacements = {
FloatingUnrealizedPnl: 'NewFields.FloatingUnrealizedPnl',
'position.InterestPnL': 'NewFields.OrdinaryInterestPnl',
MarginInterestGain: 'NewFields.MarginInterestGain',
MarginInterestLoss: 'NewFields.MarginInterestLoss',
MaturityNettingValuation: 'NewFields.MaturityNettingValuation',
PeriodPaymentValuation: 'NewFields.PeriodPaymentValuation'
};
const newFields = [
'NewFields.UnderlyingDirection',
'NewFields.UnderlyingCode',
'NewFields.InitialPrice',
'NewFields.NotionalQuantity',
'NewFields.ContractStartDate',
'NewFields.ContractMaturityDate',
'NewFields.InterestBenchmark',
'NewFields.InterestRatePrice',
'NewFields.OpeningClosingFee'
];
expect(newColumns).toHaveLength(oldColumns.length + 9);
Object.entries(replacements).forEach(([oldName, newName]) => {
expect(newNames).toContain(newName);
expect(newNames).not.toContain(oldName);
expect(newColumns.find(column => column.name === newName).label)
.toBe(oldColumns.find(column => column.name === oldName).label);
});
oldNames
.filter(oldName => !Object.prototype.hasOwnProperty.call(replacements, oldName))
.forEach(oldName => expect(newNames).toContain(oldName));
newFields.forEach(field => expect(newNames).toContain(field));
});
test('index=2 保留旧 endpoint/configindex=3 使用独立 endpoint/config 且界面不启用分组', () => {
expect(loaded.source).toContain("queryurl = '/swaptrade2/EodSwapRiskQuery';");
expect(loaded.source).toContain("cloumnTargetName = \"eodSwapList\";");
expect(loaded.source).toContain("queryurl = '/swaptrade2/EodSwapRiskNewQuery';");
expect(loaded.source).toContain("cloumnTargetName = \"eodSwapRiskNewList\";");
expect(loaded.source).toContain('eodSwapRiskNewExportColumnNames');
expect(loaded.source).not.toMatch(/main\.initCollapsibleGroupHeaders\s*\(/);
expect(eodSwapGroupConfig).not.toBe(eodSwapRiskNewGroupConfig);
expect(eodSwapRiskNewGroupConfig.some(group => group.columns.includes('NewFields.InitialPrice'))).toBe(true);
});
});