从分数到位次,从学校到专业——智能匹对模块的设计迭代

需求

高考出分后,考生同时知道自己的分数和全省位次。v2.1 的查询功能让用户能查到任何学校任何专业的历年录取线,但致命问题是——考生不知道自己的分能上什么学校,要一个个去翻。

市场上成熟的志愿填报 App 都有这个功能:输入分数,自动推荐冲/稳/保院校。我们也得做。

第一次方案:分数匹配

最初的想法很简单——用户输入分数,跟各校三年投档线比对,分三档:

档位 条件
用户分 - 学校均分 ∈ [-30, -5)
差值 ∈ [-5, +15]
差值 > +15

数据直接复用现有的 preloadData 缓存,客户端纯计算,瞬间出结果。

被否决了。 原因:每年试卷难度不同,今年 620 分可能相当于去年 600 分的排名。用分数跨年比较,天然不准。

第二次方案:位次匹配,学校级

既然位次跨年稳定,就改用位次。用户输入位次,系统比对每所学校在你生源地、你科类下的三年录取位次。

更进一步,不用绝对位次差(5000 名的差距在河南和北京含义完全不同),改用百分比阈值

diff% = (考生位次 - 学校均位次) / 学校均位次 × 100%

保底: diff% < -8%    (考生明显优于学校)
稳妥: -8% ≤ diff% ≤ +15%
冲刺: +15% < diff% ≤ +30%
排除: diff% > +30%

又被否决了。 因为"学校位次"到底取哪个?一所学校几十个专业,位次从 3500 到 15000。取最差专业的位次作为学校门槛——用户位次 12000,给重庆大学标"保底"。但他只能上哲学,根本进不了计算机。

这个"保底"标签在误导用户。

第三次方案:位次匹配,专业级

这才是对的——匹配对象应该是专业,不是学校

云函数遍历全省三年全部 admission 数据(~40 万条专业记录),对每个 (学校, 专业, 科类) 三元组计算加权均位次,逐专业分档,再按学校聚合返回。

用户看到的不再是"重庆大学 保底",而是:

🏫 重庆大学  [985] [重庆]
─────────────────────────
保底:哲学(均位次 15000, -20%)
稳妥:土木(均位次 8000, +6%)  数学(均位次 7800, +9%)
冲刺:计算机(均位次 3500, +143%)
─────────────────────────
查看院校详情 →

每个学校卡片内直接展示哪些专业能稳上、哪些要冲。

架构

40 万条专业数据 → 客户端扛不住(1MB 传输限制 + 手机算力)。改为服务端计算:

首页选省份  preloadData(已有云函数,新增 +30 行)
              └→ 后台写入 _match_cache(专业位次预缓存)

用户点「智能匹对」→ 输入位次 + 科类
              └→ matchSchools 云函数
                     _match_cache(命中率 >95%~100ms
                    逐专业加权均位次  分档
                    按学校聚合,每档 top 5 专业
                    返回三档结果

首次匹配 ~300ms,后续切换筛选条件客户端本地过滤,<10ms。

一个编译坑

页面写好后,首页变成空白。排查发现是 require('../../utils/match') 导致微信开发者工具编译失败——项目中其他页面都没在顶层用 require。把工具函数内联到页面 JS 中,同时避免箭头函数/async/await/模板字符串等 ES6 特性,解决。

云函数也踩了坑:config.json 必须单行,package.json 必须显式声明 wx-server-sdk 依赖,否则超时只有 3 秒、SDK 找不到。

小结

这次迭代的核心教训:

  1. 匹配对象决定实用性。用户关心的是"我能上什么专业",不是"我能进哪个学校"。学校级匹配看似省事,实际产出的信息价值很低。

  2. 位次 > 分数。高考分数跨年不可比,位次才是硬通货。感谢现有数据里 m 字段带 rank,不需要额外爬一分一段表。

  3. 百分比 > 绝对值。同样 5000 名的分差,在河南和北京含义天差地别。百分比阈值自动适配各省考生规模。

  4. 免责声明不是点缀。算法预测不能当录取保证,页面上黄色警示框必须醒目。

整个模块从方案设计到交付,总共新增了 1 个云函数、4 个页面文件,修改了 2 个云函数和 3 个首页文件。v2.2 的乐道志愿,现在有了真正意义上的"智能"。

Links

Social