verify-policy-detail-index.mjs 4.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * 验证「点卡片 → 打开哪一条详情」的下标反查逻辑。
  3. *
  4. * 背景(2026-09-17 用户报的 bug):同一条政策下的多个申报事项会各生成一张卡片,
  5. * 但适配层把 `title` 与 `declaration_item` 都取自 `card.name.text`(只有政策名),
  6. * 后端又不下发 `id`/`policy_id`(实测恒为 undefined)——这些卡片在字段上**无法区分**。
  7. * 原先的 findIndex 只要任一条件命中就返回,于是点第二张卡片也跳到第一张,
  8. * 表现为「两张卡片的详情内容一模一样」。
  9. *
  10. * 验证的不变量:
  11. * ① 卡片区点击(originalData 是 policies 里的同一对象)必须精确命中自己那一条
  12. * ② 字段比对**只有唯一命中**才采信;多个候选时返回 -1(交给兜底,不张冠李戴)
  13. *
  14. * 怎么跑(在项目根目录):
  15. * npx esbuild harness/tools/_entry-policy-match.ts --bundle --format=esm \
  16. * --outfile=harness/tools/_policy-match.mjs
  17. * node harness/tools/verify-policy-detail-index.mjs
  18. */
  19. import { resolvePolicyDetailIndex } from './_policy-match.mjs';
  20. let pass = 0;
  21. let fail = 0;
  22. const check = (name, cond, extra = '') => {
  23. if (cond) {
  24. pass++;
  25. console.log(' ok ' + name);
  26. } else {
  27. fail++;
  28. console.log(' FAIL ' + name + ' ' + extra);
  29. }
  30. };
  31. // ── 复现用户场景:同一政策的两个申报事项 ────────────────────────────────
  32. // 适配层产出的两个条目:declaration_item 都是政策名,都没有 id
  33. const POLICY_NAME = '关于进一步完善本区创业扶持政策有关事项的通知';
  34. const rawA = { declaration_item: POLICY_NAME, support_objects: '本区户籍创业人员' };
  35. const rawB = { declaration_item: POLICY_NAME, support_objects: '区级考核优秀/良好/合格基地' };
  36. const policies = [
  37. { declaration_item: POLICY_NAME, originalData: rawA, support_objects: rawA.support_objects },
  38. { declaration_item: POLICY_NAME, originalData: rawB, support_objects: rawB.support_objects },
  39. ];
  40. console.log('场景:同政策的两个申报事项,declaration_item 与 id 都无法区分\n');
  41. console.log('【1】卡片区点击(对象同一性——精确路径)');
  42. check('点第 1 张 → 下标 0', resolvePolicyDetailIndex(policies, policies[0]) === 0, String(resolvePolicyDetailIndex(policies, policies[0])));
  43. check(
  44. '点第 2 张 → 下标 1(**修复前这里会返回 0**)',
  45. resolvePolicyDetailIndex(policies, policies[1]) === 1,
  46. String(resolvePolicyDetailIndex(policies, policies[1]))
  47. );
  48. console.log('\n【2】字段无法区分时不得猜(唯一性要求)');
  49. // 一个不在 policies 里的对象,但 declaration_item 与两张卡片都相同
  50. const foreign = { originalData: { declaration_item: POLICY_NAME }, declaration_item: POLICY_NAME };
  51. check(
  52. '外来条目 + 两个候选 → 返回 -1(不张冠李戴,交给兜底)',
  53. resolvePolicyDetailIndex(policies, foreign) === -1,
  54. String(resolvePolicyDetailIndex(policies, foreign))
  55. );
  56. console.log('\n【3】字段唯一命中时仍应采信(退化路径保留)');
  57. const uniqueRaw = { declaration_item: '某个独占的申报事项' };
  58. const uniquePolicies = [
  59. { declaration_item: POLICY_NAME, originalData: rawA },
  60. { declaration_item: '某个独占的申报事项', originalData: uniqueRaw },
  61. ];
  62. check(
  63. '外来条目 + 唯一 declaration_item 命中 → 正确的下标',
  64. resolvePolicyDetailIndex(uniquePolicies, { originalData: { declaration_item: '某个独占的申报事项' } }) === 1,
  65. String(resolvePolicyDetailIndex(uniquePolicies, { originalData: { declaration_item: '某个独占的申报事项' } }))
  66. );
  67. const idRaw = { id: 'k_abc123' };
  68. const idPolicies = [
  69. { declaration_item: POLICY_NAME, originalData: rawA },
  70. { declaration_item: POLICY_NAME, originalData: idRaw },
  71. ];
  72. check(
  73. '按 id 唯一命中 → 正确的下标',
  74. resolvePolicyDetailIndex(idPolicies, { originalData: { id: 'k_abc123' } }) === 1,
  75. String(resolvePolicyDetailIndex(idPolicies, { originalData: { id: 'k_abc123' } }))
  76. );
  77. check(
  78. 'policy_id 同样可用',
  79. resolvePolicyDetailIndex(idPolicies, { originalData: { policy_id: 'k_abc123' } }) === 1,
  80. String(resolvePolicyDetailIndex(idPolicies, { originalData: { policy_id: 'k_abc123' } }))
  81. );
  82. console.log('\n【4】边界');
  83. check('policies 为空 → -1', resolvePolicyDetailIndex([], policies[0]) === -1);
  84. check('policies 为 null → -1', resolvePolicyDetailIndex(null, policies[0]) === -1);
  85. check('item 为 null → -1', resolvePolicyDetailIndex(policies, null) === -1);
  86. check('完全对不上 → -1', resolvePolicyDetailIndex(policies, { originalData: { declaration_item: '不存在的事项' } }) === -1);
  87. console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
  88. process.exit(fail ? 1 : 0);