- 生产接线处补注释,追溯到具体历史 bug (dcf649f2/20ea93d8/3c5f25a5/f873239a) - swapCalc.js 注明哪些函数已接入生产、哪些是纯交叉校验规格 - 新增 guard_arch.js 架构闸门:扫描提交新增行,拦截 Vue 组件内联金额计算 - pre-commit hook 串行运行 单元测试 + 架构闸门
123 lines
4.5 KiB
JavaScript
123 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* guard_arch.js — 前端架构闸门(零依赖,纯 Node)
|
||
* ============================================================================
|
||
* 目的:防止在 Vue 组件方法里【新增】手写「金额 / 精度」计算(toFixed / _.round /
|
||
* Math.round / × multiplier 缩放等),避免又长出一份「不可测的内联公式」。
|
||
*
|
||
* 规则:仅扫描【本次提交改动中新增/修改的行】(git diff 的 + 行),且位于
|
||
* wwwroot/Scripts/app/ 并定义 new Vue(...) 的文件。对其中每一行「金额算术」,
|
||
* 若该行没有路由到某个 *Calc 模块(含 SwapCalc.),则判为违规 → 退出码 1。
|
||
*
|
||
* 设计要点:
|
||
* - 只查「新增/修改的行」,不查历史存量。这样不会阻断对存量文件(如仍含内联计算的
|
||
* unwindSwapTrade.js)的正常改动,只拦「新写的内联金额公式」。
|
||
* - 已外置计算逻辑的组件(incomeSwapTrade.js / swapTradeEdit.js)通过调用 SwapCalc.*
|
||
* 保持合规;新代码必须把金额计算放进 *Calc 模块(参考 swapCalc.js)。
|
||
* - 注释行会被剥离后再判断,避免注释里的样例文字误报。
|
||
* - 无需 jest / npm install,和 _shim_run.js 同属零依赖守卫。
|
||
*
|
||
* 用法:node guard_arch.js (一般在 pre-commit / CI 中自动调用)
|
||
* ============================================================================
|
||
*/
|
||
'use strict';
|
||
|
||
const { execSync } = require('child_process');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// fe-tests -> YLErpWeb -> zszq-trs(仓库根)
|
||
const ROOT = path.resolve(__dirname, '..', '..');
|
||
|
||
// 金额 / 精度算术模式(命中即怀疑)
|
||
const MONEY_PATTERNS = [
|
||
/\.toFixed\s*\(/, // 显示/入库精度
|
||
/_\.round\s*\(/, // lodash 四舍五入
|
||
/Math\.round\s*\(/, // 原生四舍五入
|
||
/\*\s*this\.multiplier/, // 价格 × 乘数缩放(dcf649f2 / 20ea93d8 类)
|
||
/\*\s*thisObj\.multiplier/,
|
||
];
|
||
|
||
// 合规标记:行内引用了某个 *Calc 模块(如 SwapCalc.),视为已外置
|
||
const COMPLIANT = /Calc\./;
|
||
|
||
function tryCmd(cmd) {
|
||
try {
|
||
return execSync(cmd, { cwd: ROOT }).toString().trim();
|
||
} catch (e) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function changedAppVueFiles() {
|
||
const listCmd = 'git diff --cached --name-only --diff-filter=ACM -- "*.js"';
|
||
let out = tryCmd(listCmd);
|
||
if (!out) out = tryCmd('git diff --name-only --diff-filter=ACM -- "*.js"');
|
||
if (!out) return [];
|
||
return out.split('\n').filter((f) => {
|
||
if (!/wwwroot[\\/]Scripts[\\/]app[\\/]/.test(f)) return false;
|
||
const full = path.join(ROOT, f);
|
||
if (!fs.existsSync(full)) return false;
|
||
try {
|
||
return /new\s+Vue\s*\(/.test(fs.readFileSync(full, 'utf8'));
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 取文件在本次提交中【新增/修改】的行(git diff 的 + 行,排除 +++ 文件头)
|
||
function addedLines(file) {
|
||
const quoted = JSON.stringify(file);
|
||
let out = tryCmd(`git diff --cached -U0 -- ${quoted}`);
|
||
if (!out) out = tryCmd(`git diff -U0 -- ${quoted}`);
|
||
if (!out) return [];
|
||
return out.split('\n')
|
||
.filter((l) => l.startsWith('+') && !l.startsWith('+++'))
|
||
.map((l) => l.slice(1));
|
||
}
|
||
|
||
function stripComment(line) {
|
||
// 去掉 /* */ 块注释与 // 行内注释,避免注释里的样例文字误报
|
||
return line.replace(/\/\*.*?\*\//g, '').replace(/\/\/.*$/, '').trim();
|
||
}
|
||
|
||
function main() {
|
||
const files = changedAppVueFiles();
|
||
if (files.length) {
|
||
console.log(`[guard_arch] 扫描 ${files.length} 个改动的 Vue 组件文件的新增/修改行`);
|
||
}
|
||
|
||
let violations = 0;
|
||
for (const file of files) {
|
||
const added = addedLines(file);
|
||
const found = [];
|
||
added.forEach((rawLine) => {
|
||
const code = stripComment(rawLine);
|
||
if (!code) return;
|
||
const hasMoney = MONEY_PATTERNS.some((p) => p.test(code));
|
||
if (hasMoney && !COMPLIANT.test(code)) {
|
||
found.push(` + ${rawLine.trim()}`);
|
||
}
|
||
});
|
||
|
||
if (found.length) {
|
||
violations += found.length;
|
||
console.log(`[guard_arch] 违规 ${file} 发现【新增】内联金额计算(应路由到 *Calc 模块):`);
|
||
found.forEach((f) => console.log(f));
|
||
}
|
||
}
|
||
|
||
if (violations > 0) {
|
||
console.log(
|
||
`\n[guard_arch] 发现 ${violations} 处【新增】内联金额计算, 提交被阻断。` +
|
||
`请把金额/精度计算抽到 *Calc 模块(参考 swapCalc.js)。`
|
||
);
|
||
process.exit(1);
|
||
}
|
||
console.log('[guard_arch] OK: 改动的 Vue 组件未新增内联金额计算。');
|
||
process.exit(0);
|
||
}
|
||
|
||
main();
|