| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- /**
- * 前端 `/update` 入口的两向变异驱动。
- *
- * 每条形变都:① 断言锚点**恰好命中一次**(命中 0 次或多次都不动文件);② 打补丁;
- * ③ 跑完由调用方 `restore`,`restore` 从 `.mut-bak` 原样拷回并复核 sha256。
- * ★ 「还原」本身也要被验证 —— 所以 restore 会打印还原后的 sha256,由外面的脚本与基线对撞。
- *
- * 用法:node fe-update-mutate.mjs <mutationId|restore> <目标文件>
- */
- import fs from "node:fs";
- import crypto from "node:crypto";
- const PAGE = "D:/project2/payment-platform/frontend/src/views/module_payment/collection/onboarding/index.vue";
- const ID = process.argv[2];
- const MUTATIONS = {
- // 红向 ①:把「减去两个」拆掉 ⇒ 更新报文会带上 merchant_name_business / lease_contract
- "M-U1": {
- file: PAGE,
- from: "const UPDATE_KEYS: string[] = CREATE_KEYS.filter((k) => !UPDATE_EXCLUDED_KEYS.includes(k));",
- to: "const UPDATE_KEYS: string[] = CREATE_KEYS.slice();",
- },
- // 红向 ②:把白名单换成整表摊开 ⇒ category / tenant_id / rate_code 全都会发出去
- "M-U2": {
- file: PAGE,
- from: ` const body: Record<string, string> = { mch_no: form.mch_no.trim() };
- for (const key of UPDATE_KEYS) {
- const value = (form[key] ?? "").trim();
- if (value !== "") body[key] = value;
- }
- return body;`,
- to: ` const body: Record<string, string> = { mch_no: form.mch_no.trim() };
- for (const key of Object.keys(form)) {
- const value = (form[key] ?? "").trim();
- if (value !== "") body[key] = value;
- }
- return body;`,
- },
- // 红向 ③:渲染投影失效 ⇒ 更新模式下那两格会渲染出来(按下去必 500)
- "M-U3": {
- file: PAGE,
- from: ` return mode.value === "create"
- ? s.fields
- : s.fields.filter((f) => !UPDATE_EXCLUDED_KEYS.includes(f.key));`,
- to: ` return s.fields;`,
- },
- // 无害向:给更新腿的 catch 加一句注释 —— 行为**应当完全不变**(这一条是「刻意钝感」的那一侧)
- "M-U5": {
- file: PAGE,
- from: ` } catch {
- // 失败文案由拦截器统一弹(两道封盖的 400、非本租户的 404、上游失败的 502 都在 \`msg\` 里)`,
- to: ` } catch {
- // MUTANT-HARMLESS:只加一行注释
- // 失败文案由拦截器统一弹(两道封盖的 400、非本租户的 404、上游失败的 502 都在 \`msg\` 里)`,
- },
- };
- /**
- * ★ 这两条是给 **`vue-tsc`** 用的,不是给执行级台子用的:`ts:check` 在本仓有一个**非零历史基线**
- * ⇒ 「前后输出一模一样」也可能是因为 vue-tsc **压根没看我新加的文件**。要主张「没引入新错」,
- * 就必须先证明它**看得见**这两个文件。
- */
- const API = "D:/project2/payment-platform/frontend/src/api/module_payment/onboarding.ts";
- MUTATIONS["M-T1"] = {
- file: PAGE,
- from: `const mode = ref<"create" | "update">("create");`,
- to: `const mode = ref<"create" | "update">("create");\nconst __tsProbe: number = "故意写错的类型";`,
- };
- MUTATIONS["M-T2"] = {
- file: API,
- from: `export function updateOnboardingMerchant(data: Record<string, unknown>) {`,
- to: `const __tsProbe2: number = "故意写错的类型";\nexport function updateOnboardingMerchant(data: Record<string, unknown>) {`,
- };
- const sha = (f) => crypto.createHash("sha256").update(fs.readFileSync(f)).digest("hex");
- if (ID === "restore") {
- for (const f of new Set(Object.values(MUTATIONS).map((m) => m.file))) {
- const bak = f + ".mut-bak";
- if (fs.existsSync(bak)) {
- fs.copyFileSync(bak, f);
- fs.unlinkSync(bak);
- console.log(`restored ${f} sha256=${sha(f)}`);
- }
- }
- process.exit(0);
- }
- const m = MUTATIONS[ID];
- if (!m) throw new Error("未知变异:" + ID);
- const bak = m.file + ".mut-bak";
- if (!fs.existsSync(bak)) fs.copyFileSync(m.file, bak);
- const src = fs.readFileSync(m.file, "utf8");
- const hits = src.split(m.from).length - 1;
- if (hits !== 1) throw new Error(`[${ID}] 锚点命中 ${hits} 次,期望恰好 1 次 —— 文件未被修改`);
- fs.writeFileSync(m.file, src.replace(m.from, m.to));
- console.log(`[${ID}] patched ${m.file} sha256=${sha(m.file)}`);
|