refactor(login): move match-route logic to dedicated service and fix import

- 提取登录回跳、产品对比的路由匹配逻辑至独立的 match-route.uts 服务
- 修复登录页面导入对比服务的错误,改为导入新的 match-route 模块
- 保持原有的路由参数校验和跳转逻辑不变,仅重构代码结构
This commit is contained in:
hujun
2026-10-12 09:28:09 +08:00
parent 25cb06b71c
commit b418969e92
39 changed files with 49277 additions and 119 deletions
+82
View File
@@ -0,0 +1,82 @@
// Run after MP-WEIXIN compilation; exercise the generated report adapter without network or application initialization.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const root = path.resolve(__dirname, '..');
const built = path.join(root, 'unpackage/dist/dev/mp-weixin/packages/finance/services/records.js');
assert.ok(fs.statSync(built).mtimeMs >= fs.statSync(path.join(root, 'packages/finance/services/records.uts')).mtimeMs, 'Compile MP-WEIXIN before testing');
class JsonObject {
constructor(value) { Object.assign(this, value); }
getString(key) { return typeof this[key] === 'string' ? this[key] : null; }
getNumber(key) { return typeof this[key] === 'number' ? this[key] : null; }
getBoolean(key) { return typeof this[key] === 'boolean' ? this[key] : null; }
}
class UtsType { static initProps(options) { return options; } }
function awaiter(thisArg, args, PromiseType, generator) {
return new (PromiseType || Promise)((resolve, reject) => {
const iterator = generator.apply(thisArg, args || []);
const step = result => result.done ? resolve(result.value) : Promise.resolve(result.value).then(value => advance('next', value), error => advance('throw', error));
const advance = (method, value) => { try { step(iterator[method](value)); } catch (error) { reject(error); } };
advance('next');
});
}
const exported = {};
let response;
vm.runInNewContext(fs.readFileSync(built, 'utf8'), {
exports: exported,
require(module) {
if (module.endsWith('/common/vendor.js')) return { UTS: { UTSType: UtsType, arrayFind: (items, predicate) => items.find(predicate) ?? null }, UTSJSONObject: JsonObject, __awaiter: awaiter };
if (module.endsWith('/services/assessment.js')) return { draftText: (item, key) => typeof item[key] === 'string' ? item[key] : '' };
if (module.endsWith('/services/api.js')) return { requestJson: async () => response };
if (module.endsWith('/financing-grade.js')) return { financingGradeFrom: () => ({}) };
if (['/services/assessment-flow.js', '/services/contribution.js', '/services/money.js'].some(suffix => module.endsWith(suffix))) return {};
throw Error('Unexpected dependency: ' + module);
}
}, { filename: built });
const field = (key, label, state = 'missing') => new JsonObject({ fieldKey: key, label, state });
const reason = (id, code, keys, issues = []) => new JsonObject({ basisId: id, code, fieldRefs: keys, issueRefs: issues });
const advice = (text, refs) => new JsonObject({ text, evidenceRefs: refs });
const debt = field('totalDebtBalance', '欠款总余额');
const income = field('monthlyIncome', '月收入');
const debtReason = reason('missing-totalDebtBalance', 'MISSING_FIELD', ['totalDebtBalance']);
let checks = 0;
const cases = [];
function verify(name, suggestions, fields, basis, expected) {
cases.push({ name, suggestions, fields, basis, expected });
}
async function verifySavedReport({ name, suggestions, fields, basis, expected }) {
const snapshot = JSON.stringify({ suggestions, fields, basis });
response = new JsonObject({
assessmentId: '900001', revisionId: '900002', revisionNo: 1, isPreviousResult: false, currentStatus: 'succeeded',
resultVersion: 2, reportType: 'financing_needs_analysis', inputVersion: 3, generatedAt: '2026-10-09T08:40:00Z',
facts: [], demand: new JsonObject({}), matching: new JsonObject({ status: 'unavailable' }), summary: '合成报告',
completeness: new JsonObject({ status: 'evaluated', percent: 90, providedCount: 10, applicableCount: 11, items: fields }),
readiness: new JsonObject({ status: 'evaluated', label: '合成准备状态', basis }),
improvements: suggestions, strengths: [], riskWarnings: [], systemWarnings: [], disclaimer: '合成报告', modelCode: 'synthetic', financingGrade: null
});
const actual = await exported.fetchAnalysisReport('900001', '900002');
assert.deepEqual(Array.from(actual.priorityItems), expected, name);
assert.equal(JSON.stringify({ suggestions, fields, basis }), snapshot, name + ': saved report inputs remain unchanged');
checks += 2;
}
verify('same missing field: AI advice replaces fallback', [advice('欠款总余额尚未填写,建议补充。', ['readiness:missing-totalDebtBalance'])], [debt], [debtReason], ['欠款总余额尚未填写,建议补充。']);
verify('fact and readiness references resolve to the same problem', [advice('请补齐欠款', ['fact:totalDebtBalance']), advice('欠款待补充', ['readiness:missing-totalDebtBalance'])], [debt], [debtReason], ['请补齐欠款']);
verify('same readiness problem with different wording appears once', [advice('请补齐欠款', ['readiness:missing-totalDebtBalance']), advice('欠款待补充', ['readiness:missing-totalDebtBalance'])], [debt], [debtReason], ['请补齐欠款']);
verify('uncovered missing fields remain visible', [advice('请补齐欠款', ['readiness:missing-totalDebtBalance'])], [debt, income], [debtReason], ['请补齐欠款', '补充月收入']);
verify('empty advice does not suppress real missing fields', [advice(' ', ['readiness:missing-totalDebtBalance'])], [debt], [debtReason], ['补充欠款总余额']);
verify('provided and inapplicable fields create no fallback', [], [field('company', '单位', 'provided'), field('annualRevenue', '营收', 'not_applicable')], [], []);
verify('invalid format uses verification fallback', [], [field('totalDebtBalance', '欠款总余额', 'invalid')], [], ['核实欠款总余额']);
verify('invalid readiness advice replaces verification fallback', [advice('请核实欠款格式', ['readiness:invalid-totalDebtBalance'])], [field('totalDebtBalance', '欠款总余额', 'invalid')], [reason('invalid-totalDebtBalance', 'INVALID_FIELD', ['totalDebtBalance'])], ['请核实欠款格式']);
verify('different problems on the same field remain separate', [advice('请核实额度格式', ['readiness:invalid-creditCardUsedLimit']), advice('已用额度超过总额度', ['readiness:issue-card-limit'])], [field('creditCardUsedLimit', '已使用总额度', 'invalid')], [reason('invalid-creditCardUsedLimit', 'INVALID_FIELD', ['creditCardUsedLimit']), reason('issue-card-limit', 'CREDIT_CARD_USED_EXCEEDS_LIMIT', ['creditCardUsedLimit', 'creditCardTotalLimit'], ['card-limit'])], ['请核实额度格式', '已用额度超过总额度']);
verify('combined advice covers each of its missing fields', [advice('请补齐欠款和收入', ['readiness:missing-totalDebtBalance', 'readiness:missing-monthlyIncome'])], [debt, income], [debtReason, reason('missing-monthlyIncome', 'MISSING_FIELD', ['monthlyIncome'])], ['请补齐欠款和收入']);
verify('new problem in overlapping advice is retained', [advice('请补齐欠款', ['readiness:missing-totalDebtBalance']), advice('请补齐欠款和收入', ['readiness:missing-totalDebtBalance', 'readiness:missing-monthlyIncome'])], [debt, income], [debtReason, reason('missing-monthlyIncome', 'MISSING_FIELD', ['monthlyIncome'])], ['请补齐欠款', '请补齐欠款和收入']);
verify('unknown evidence cannot hide a missing field', [advice('请核实其他资料', ['readiness:unknown'])], [debt], [], ['请核实其他资料', '补充欠款总余额']);
verify('surrounding whitespace and duplicate evidence do not create duplicate advice', [advice(' 请补齐欠款 ', ['readiness:missing-totalDebtBalance', 'readiness:missing-totalDebtBalance']), advice('请补齐欠款', ['readiness:missing-totalDebtBalance'])], [debt], [debtReason], ['请补齐欠款']);
verify('ordinary fact evidence is not a problem identity', [advice('保留收入证明材料', ['fact:monthlyIncome']), advice('控制每月还款负担', ['fact:monthlyIncome'])], [field('monthlyIncome', '月收入', 'provided')], [], ['保留收入证明材料', '控制每月还款负担']);
verify('same known consistency issue is consolidated', [advice('核实信用卡额度', ['readiness:issue-card-limit']), advice('已用额度超过总额度,请核实', ['readiness:issue-card-limit'])], [], [reason('issue-card-limit', 'CREDIT_CARD_USED_EXCEEDS_LIMIT', ['creditCardUsedLimit', 'creditCardTotalLimit'], ['card-limit'])], ['核实信用卡额度']);
verify('unknown reference alone cannot equate different problems', [advice('准备收入证明', ['readiness:unknown']), advice('核实负债资料', ['readiness:unknown'])], [debt], [], ['准备收入证明', '核实负债资料', '补充欠款总余额']);
(async () => {
for (const entry of cases) await verifySavedReport(entry);
console.log('PASS ' + checks + ' report priority checks; no network or database');
})().catch(error => { console.error(error); process.exitCode = 1; });