怎么用 这是格式与深度的参考,不是让你照抄的模板。数字全部是示例,直接抄进简历一问就穿——必须换成你自己的真实数据,而且要能说清是怎么测出来的。下面每个模块都有「数字是怎么测的」和「面试追问」两段,写不出那两段的模块就不要往简历上写

面试题库(电商 / 短视频 / 社区 / 本地生活 / 旅游 / 企业服务 多业务场景)

项目背景设定 国际旅游预订平台的行程助手,uni-app 打 App 与小程序。它服务的是「已经订好、正在出行」的用户:查看行程、出示凭证入住入园、接收出行提醒。预订与下单链路在 旅游 · uni-app · 旅游预订端 那一页(选日期、看价格、下单等确认),这一页是出行中——两个阶段的用户处境完全不同。
为什么选这三个模块 出行中的用户处境是整个题库里最恶劣的可能在境外没有网络(漫游没开、当地卡还没买、酒店 Wi-Fi 要先登录)、可能在时区之间移动而他要办的事偏偏是不能失败的(到酒店前台要出示凭证、赶航班要准时)。三个模块正好对应:行程数据的离线可用(无网也要能看到凭证和地址,这是硬需求不是优化)、时区与出行提醒(「明天早上 8 点的航班」到底是哪个时区的 8 点,算错就是误机)、凭证的线下出示(前台扫码这一刻的失败是最贵的失败)。这一页的共同主题是:假设网络不可用、假设时区会变、假设失败发生在最不能失败的时刻。

模块一:行程数据的离线可用

  1. 行程数据的离线可用(关键数据主动预缓存 + 离线优先渲染 + 数据新鲜度显式标注 + 分级缓存策略 + 出行前主动预热)★★★
    简历这样写 行程数据的离线可用设计(临近出行时主动预缓存关键数据与凭证图片 + 一律本地优先渲染再后台刷新 + 数据新鲜度与「离线展示」状态显式标注 + 按重要性分级的缓存与淘汰策略 + 离线态下明确区分「可用」与「需联网」的功能):出行中的用户常处于无网络或弱网环境(境外漫游未开通、当地网络未就绪、酒店网络需登录),而他要做的事恰恰不能失败(出示凭证入住、按时赶航班),因此把行程数据从「按需请求」改为临近出行时主动预缓存(行程详情、地址与联系电话、凭证图片与二维码内容、退改政策),渲染一律本地优先再后台刷新;界面显式标注数据的更新时间与「当前为离线展示」,避免用户在无网时误以为看到的是最新信息;缓存按重要性分级(凭证与地址优先级最高、长期保留;推荐内容可随时淘汰),并在出行前主动预热;离线态下明确区分哪些功能可用、哪些需要联网而非统一报错。改造后无网络时凭证与地址仍可查看,「到了酒店打不开凭证」的反馈由预缓存与离线优先消除
    展开完整拆解
    为什么要这么设计

    这个模块的前提和别的移动端页面完全不同:它的用户很可能没有网络,而且这不是偶发情况,是常态。

    境外出行的网络处境很具体:下了飞机漫游没开通、当地电话卡还没买到、酒店 Wi-Fi 需要先在浏览器里登录(而登录页在没网时打不开)、地铁和景区里信号差而用户在这些时刻恰恰最需要看行程——他要知道酒店地址怎么念给出租车司机、要出示入住凭证、要确认下一段航班的时间。

    第一版把它当普通页面做:进页面请求接口、拿到数据渲染。无网时显示「网络异常,请检查网络」。这个体验在境外是灾难性的——用户站在酒店前台,打不开凭证,而他没有网络去打开它,只能求助前台的 Wi-Fi 或者用别人的手机热点。我们收到的反馈里这一类的情绪最强。

    所以这一页的设计原则是「假设网络不可用」,而不是「网络不可用时降级」——顺序反了,实现方式就完全不同。

    第一是主动预缓存,而不是按需缓存。按需缓存(用户看过就缓存)的问题是用户往往在需要的时候才第一次看——他到了酒店才点开凭证,那时已经没网了。所以要临近出行时主动把关键数据下载下来:行程详情、每个项目的地址与联系电话、凭证图片与二维码内容、退改政策。预缓存的时机是「出行前还有网络的时候」,比如订单确认后、出行前一天。

    第二是渲染一律本地优先。不是「先请求,失败了读缓存」,而是「先读缓存渲染,同时后台请求刷新」这两种顺序的差别在弱网下极其明显:前者要等请求超时才显示内容(可能十几秒),后者立刻有内容。

    第三是新鲜度必须显式标注。本地优先带来一个新问题:用户看到的可能是旧数据。而行程数据是会变的(航班改时间、酒店换房型)。所以要明确显示「更新于 X」以及「当前为离线展示」——让用户知道这份数据的时效性,而不是让他以为看到的一定是最新的。这一条和值班大屏「数据陈旧要显式提示」是同一条原则。

    第四是缓存分级。端上存储有限,不能什么都长期存。凭证与地址优先级最高、长期保留;行程详情次之;推荐内容、图片轮播这类可随时淘汰分级的依据是「无网时用户最需要什么」。

    第五是离线态下要区分「可用」与「需联网」的功能,而不是统一报错。查看凭证、看地址、看行程可以离线;而修改订单、联系客服、实时航班动态必须联网要在界面上明确区分,不要让用户去点一个注定失败的按钮。

    整体链路
    前提:用户很可能没有网络,而且这是常态不是偶发 │ └─ 境外出行的网络处境很具体 下了飞机漫游没开通 当地电话卡还没买到 酒店 Wi-Fi 要先在浏览器登录(而登录页没网时打不开) 地铁和景区里信号差 → 而这些时刻恰恰最需要看行程 酒店地址要念给出租车司机 要出示入住凭证 要确认下一段航班时间 第一版把它当普通页面做的后果 ├─ 进页面请求接口、拿到数据渲染 ├─ 无网时显示「网络异常,请检查网络」 └─ 在境外是灾难性的 用户站在酒店前台,打不开凭证 而他没有网络去打开它 只能求助前台 Wi-Fi 或别人的热点 → 我们收到的反馈里这一类情绪最强 设计原则:假设网络不可用 └─ 不是「网络不可用时降级」 顺序反了,实现方式就完全不同 一、主动预缓存,不是按需缓存 │ ├─ 按需缓存的问题 │ 用户往往在需要的时候才第一次看 │ 他到了酒店才点开凭证,那时已经没网了 │ ├─ 预缓存内容 │ 行程详情 │ 每个项目的地址与联系电话 │ 凭证图片与二维码内容 │ 退改政策 │ └─ 预缓存时机:出行前还有网络的时候 订单确认后、出行前一天 二、渲染一律本地优先 ├─ 不是「先请求,失败了读缓存」 ├─ 而是「先读缓存渲染,同时后台请求刷新」 └─ 两种顺序在弱网下差别极其明显 前者要等请求超时才显示内容(可能十几秒) 后者立刻有内容 三、新鲜度必须显式标注 │ ├─ 本地优先带来新问题:用户看到的可能是旧数据 ├─ 而行程数据会变(航班改时间、酒店换房型) │ └─ 明确显示「更新于 X」+「当前为离线展示」 让用户知道这份数据的时效性 而不是让他以为看到的一定是最新的 → 和值班大屏「数据陈旧要显式提示」同一条原则 四、缓存分级(端上存储有限) ├─ 最高:凭证、地址与电话 → 长期保留 ├─ 次之:行程详情 ├─ 可淘汰:推荐内容、图片轮播 └─ 分级依据是「无网时用户最需要什么」 五、离线态区分「可用」与「需联网」 ├─ 可离线:查看凭证、看地址、看行程 ├─ 需联网:修改订单、联系客服、实时航班动态 └─ 界面上明确区分,不要让用户去点一个注定失败的按钮 预热的触发时机 ├─ 订单确认后立刻预热一次 ├─ 出行前一天再预热一次(数据可能已变) ├─ 每次有网络且打开应用时增量刷新 └─ 预热要在 Wi-Fi 下优先执行,避免消耗用户流量
    分步拆解
    1. 先确立原则:假设网络不可用,而不是「网络不可用时降级」。顺序反了实现方式就完全不同——后者是在正常流程上加兜底,前者是把离线当主路径。
    2. 做主动预缓存而不是按需缓存。按需缓存的致命问题是用户往往在需要的时候才第一次看——到了酒店才点开凭证,那时已经没网。
    3. 预缓存的内容要覆盖「无网时真正需要的」:行程详情、地址与电话、凭证图片与二维码内容、退改政策。
    4. 预缓存的时机是「出行前还有网络的时候」。订单确认后、出行前一天,而不是等用户主动打开页面
    5. 预热优先在 Wi-Fi 下执行。预缓存会消耗流量,境外漫游流量很贵,不能不管不顾地下载。
    6. 渲染一律本地优先:先读缓存渲染,同时后台请求刷新。不是「先请求失败了读缓存」——后者要等超时才有内容,弱网下可能十几秒
    7. 显式标注数据的更新时间。本地优先意味着用户可能看到旧数据,而行程数据会变
    8. 明确显示「当前为离线展示」。让用户知道这份数据的时效性,而不是让他以为看到的一定是最新的——和值班大屏的陈旧提示是同一条原则。
    9. 缓存按重要性分级。凭证与地址最高且长期保留、行程详情次之、推荐内容与轮播图可随时淘汰
    10. 分级依据是「无网时用户最需要什么」,不是「数据量大小」。凭证图片可能不小,但它是最不能丢的。
    11. 离线态下明确区分「可用」与「需联网」的功能。查看类可离线;修改订单、联系客服、实时航班动态必须联网
    12. 需联网的功能在离线态要置灰并说明原因,不要让用户点了才失败。
    13. 每次有网络且打开应用时做增量刷新。不要每次全量拉,境外流量贵
    14. 缓存要有版本与失效机制。行程结构变化时旧缓存要能被正确升级或丢弃。
    15. 行程结束后清理缓存。已完成的行程不必长期占用存储,但要保留一段时间供用户回看凭证与账单
    关键决策与取舍

    「假设网络不可用」这个原则的顺序很关键,它不是措辞差异。如果原则是「网络不可用时降级」,实现上就是在正常请求流程上加一层 catch,失败时读缓存——而这条路径很少被测试、缓存内容也不完整(因为是按需缓存的副产物)。如果原则是「假设网络不可用」,实现上就是缓存是主数据源、网络是刷新手段离线路径就是主路径,天天在跑,不可能出问题取舍是要主动做预缓存(消耗流量与存储),而且要维护缓存的完整性与新鲜度判断依据是「无网的发生概率」——普通电商 App 里无网是偶发,加个兜底就够;境外出行场景里无网是常态,必须当主路径设计。

    本地优先渲染的代价是「可能展示旧数据」,所以新鲜度标注是它的必要配套。不标注的话,用户看到一个航班时间就以为是最新的,而实际航班可能已经改时间了——这在出行场景里的后果是误机所以「本地优先」和「新鲜度显式标注」必须一起做,只做前者是危险的这和值班大屏「宁可显示『数据已停止更新 3 分钟』,也不要显示一个看起来正常的旧数字」是完全同一条判断——只是这里的后果更严重。

    缓存分级按「无网时用户最需要什么」而不是按数据量。直觉上会优先缓存小的、淘汰大的。但凭证图片可能不小,而它恰恰是最不能丢的——用户在前台出示不了凭证,其他所有缓存都没有意义。所以分级的依据必须是业务重要性,存储不够就淘汰推荐内容和轮播图这些「有网时再拉也行」的东西。

    踩过的坑:用户在酒店前台打不开凭证。第一版无网时显示「网络异常,请检查网络」,而用户就是因为没网络才打不开——这个提示对他毫无帮助,甚至是一种嘲讽。他只能求助前台的 Wi-Fi 或者借别人的热点。我们收到的反馈里这一类情绪最强。修法是预缓存加本地优先教训是:错误提示要考虑「用户是否有能力按提示行动」——「请检查网络」在一个压根没有网络可用的场景里是无效提示,正确的做法是让功能在那个场景下压根不需要网络

    踩过的坑二:按需缓存等于没缓存。我们最初做了缓存,但是「用户看过的才缓存」而用户的行为恰恰是「到了地方才第一次点开凭证」,那时已经没网,缓存里什么都没有。修法是改为主动预缓存,在出行前还有网络时下载好教训是:缓存策略要匹配用户的真实访问模式——按需缓存假设「用户会先在有网时访问一遍」,而这个假设在出行场景里不成立

    踩过的坑三:本地优先上线后,用户看到了过期的航班时间。航班改了时间,用户在无网状态下看到的还是旧时间,而界面上没有任何提示,他按旧时间去机场。虽然最终没有造成误机(航司也发了短信),但这是很严重的隐患。修法是显式标注更新时间与离线状态,并且对「时间敏感」的项目额外提示「请确认最新时间」教训是:本地优先必须配新鲜度标注,两者是一个整体,只做前者是把风险转移给了用户。

    踩过的坑四:预缓存不管网络类型,在漫游下消耗了用户的流量。预热逻辑一开始没判断网络类型,有用户在境外漫游状态下被下载了一批凭证图片,流量费很贵,来投诉。修法是预热优先在 Wi-Fi 下执行,蜂窝网络下只拉必要的文本数据、图片延后教训是:主动下载类行为必须判断网络类型与计费环境——尤其在国际业务里,「有网络」不等于「可以随便用网络」。

    没做的部分:没做完整的离线地图(无网时也能导航到酒店)。需求很真实但离线地图包体积大、授权成本高,当时的做法是缓存地址文本与当地语言的地址(能给出租车司机看)以及一张静态地图截图。也没做行程的多人共享(同行人共享一份行程),需要账号关系与权限设计,排期没排上。

    数字是怎么测的

    离线可用性:确定性验证,而且是这个模块最该报的——断开全部网络,检查行程详情、地址与电话、凭证与二维码是否都能正常查看报的是「覆盖了哪些内容」这个清单,比任何比率有说服力。

    预缓存命中率:用户首次打开行程详情时命中预缓存的比例这个数字直接证明「主动预缓存」比「按需缓存」有效——按需缓存在这个指标上应该接近零(因为用户就是第一次看)。

    首屏时间:弱网条件下的首屏时间,改造前后对比。关键是要说明改造前是「等请求超时才显示内容」,这个归因让数字有意义。

    「打不开凭证」类反馈:相关工单或反馈的数量,改造前后对比。这是这个模块最直接的业务指标,而且工单数外部可核查。

    新鲜度标注的有效性:因看到旧数据而产生的问题数(比如按旧航班时间行动)这个数字很难直接测,诚实的表述是「加标注后未再出现相关反馈,但我们无法证明用户都注意到了标注」——承认度量的局限比编一个数字好。

    缓存占用与淘汰:行程缓存的存储占用与各级别的占比,以及淘汰触发次数。说明凭证与地址从未被淘汰过。

    预热的流量消耗:要主动报——Wi-Fi 与蜂窝下的预热数据量,说明蜂窝下只拉文本、图片延后。这是踩过投诉之后的改动,主动交代比等人问好。

    不要报「100% 离线可用」。实时航班动态、修改订单这些功能本质上需要联网,我们的设计也是明确区分的。正确表述是「哪些内容离线可用(给清单)、预缓存命中率、弱网首屏时间的变化、新鲜度标注与其度量局限、预热的网络类型策略」。

    面试追问
    Q:用户在境外没网络,怎么让他看到行程? A:核心是把原则定成「假设网络不可用」,而不是「网络不可用时降级」——这个顺序不是措辞差异。如果原则是后者,实现上就是在正常请求流程上加一层 catch、失败时读缓存,而这条路径很少被测试、缓存内容也不完整(它只是按需缓存的副产物);如果原则是前者,实现上缓存是主数据源、网络只是刷新手段离线路径就是主路径、天天在跑、不可能出问题。具体三条:主动预缓存(出行前还有网络时下载行程详情、地址与电话、凭证图片与二维码内容、退改政策);渲染一律本地优先(先读缓存渲染、同时后台刷新,而不是「先请求失败了读缓存」——后者要等超时才有内容,弱网下可能十几秒);缓存按重要性分级判断依据是「无网的发生概率」——普通电商 App 里无网是偶发、加个兜底就够;境外出行场景里无网是常态,必须当主路径设计
    Q:做了缓存为什么还是打不开? A:因为我们最初做的是「按需缓存」——而按需缓存在这个场景里等于没缓存。逻辑是「用户看过的才缓存」,而用户的行为恰恰是「到了地方才第一次点开凭证」——他站在酒店前台第一次点开,那时已经没网,缓存里什么都没有。教训是:缓存策略要匹配用户的真实访问模式——按需缓存假设「用户会先在有网时访问一遍」,而这个假设在出行场景里不成立。修法是改为主动预缓存,时机放在「出行前还有网络的时候」:订单确认后立刻预热一次、出行前一天再预热一次(数据可能已变)、每次有网且打开应用时增量刷新。这里还有一个我们踩过的相关坑:预热逻辑一开始没判断网络类型,有用户在境外漫游状态下被下载了一批凭证图片,流量费很贵,来投诉。修法是预热优先在 Wi-Fi 下执行,蜂窝网络下只拉必要的文本数据、图片延后教训是主动下载类行为必须判断网络类型与计费环境——尤其在国际业务里,「有网络」不等于「可以随便用网络」。
    Q:本地优先渲染,用户看到的是旧数据怎么办? A:必须显式标注新鲜度——「本地优先」和「新鲜度标注」是一个整体,只做前者是把风险转移给了用户。我们踩过:航班改了时间,用户在无网状态下看到的还是旧时间,而界面上没有任何提示,他按旧时间去机场虽然最终没造成误机(航司也发了短信),但这是很严重的隐患。修法是明确显示「更新于 X」和「当前为离线展示」,并且对时间敏感的项目额外提示「请确认最新时间」这和我在值班大屏上的判断是完全同一条:宁可显示「数据已停止更新 3 分钟」,也不要显示一个看起来正常的旧数字——只是这里的后果更严重(误机 vs 调度决策失误)。度量上我要诚实说明局限:「因看到旧数据而产生的问题数」很难直接测,我的表述是「加标注后未再出现相关反馈,但我们无法证明用户都注意到了标注」——承认度量的局限比编一个数字好另外离线态还要明确区分「可用」与「需联网」的功能:查看类可离线,修改订单、联系客服、实时航班动态必须联网,要置灰并说明原因,不要让用户点了才失败
    Q:端上存储有限,缓存什么、淘汰什么? A:按「无网时用户最需要什么」分级,而不是按数据量大小。直觉上会优先缓存小的、淘汰大的;但凭证图片可能不小,而它恰恰是最不能丢的——用户在前台出示不了凭证,其他所有缓存都没有意义。所以分级是:最高级是凭证、地址与联系电话,长期保留次之是行程详情可随时淘汰的是推荐内容、图片轮播这些「有网时再拉也行」的东西。依据必须是业务重要性。配套还有几件事缓存要有版本与失效机制(行程结构变化时旧缓存要能被正确升级或丢弃);行程结束后清理缓存,但要保留一段时间供用户回看凭证与账单还有一个我们没做但值得提的取舍:没做完整的离线地图(无网时导航到酒店)——需求很真实但离线地图包体积大、授权成本高,我们的替代方案是缓存地址文本与当地语言的地址(能直接给出租车司机看)以及一张静态地图截图这个替代方案的思路是:抓住「用户实际要完成的事」(让司机知道去哪),而不是「实现那个功能」(离线导航)。

模块二:时区与出行提醒

  1. 时区与出行提醒(时间一律带时区存储 + 展示按当地时区并双标注 + 提醒基于绝对时刻 + 通知权限与到达兜底 + 地理围栏提醒的能耗控制)★★★
    简历这样写 跨时区行程的时间展示与出行提醒(时间一律以绝对时刻加时区标识存储 + 展示按事件发生地当地时区并在跨时区场景双标注 + 提醒调度基于绝对时刻而非本地墙钟 + 通知权限缺失时的应用内兜底提醒 + 地理围栏提醒按行程阶段动态开关以控制能耗):跨时区出行中「明天早上 8 点的航班」存在歧义(出发地时间还是目的地时间),早期时间以本地时间字符串存储与展示,用户跨时区后看到的时间随设备时区变化而漂移,出现过对航班时间产生误解的反馈;因此把时间统一为绝对时刻加时区标识,展示时按事件发生地的当地时区渲染并在跨时区场景同时标注当地时间与用户当前所在地时间;出行提醒基于绝对时刻调度,不受设备时区变更影响;针对通知权限未开启的情况提供应用内醒目提醒与开启引导而非静默失效;地理围栏类提醒(如临近酒店提示办理入住)按行程阶段动态开关并限制围栏数量以控制能耗。改造后跨时区的时间歧义由双标注消除,提醒不再因设备时区变更而错时。
    展开完整拆解
    为什么要这么设计

    跨时区是旅游业务里最容易出错、后果又最严重的一类问题算错时间就是误机

    而它的第一个陷阱是语言上的歧义:「明天早上 8 点的航班」——是出发地的 8 点还是目的地的 8 点?对用户来说这句话是模糊的,而如果界面上只写「08:00」,用户会用自己所在地的时间去理解它

    第一版的实现犯了一个基础错误:时间以本地时间字符串存储与展示(比如「2024-06-01 08:00」)。这带来两个问题。

    一是随设备时区漂移。用户在出发地看是 8 点,飞到目的地之后设备时区变了,同一条数据渲染出来变成了另一个时间——而航班时间压根没变。用户会以为航班改时间了。

    二是提醒错时。我们的提醒是按「本地时间到点」触发的,用户跨时区后,提醒在错误的时刻响了——可能提前几小时,也可能已经过了。

    所以第一个设计是时间一律以绝对时刻加时区标识存储绝对时刻保证唯一性(不管设备在哪个时区,它指的都是同一个瞬间);时区标识用于正确展示(要按事件发生地的时区渲染,因为「航班 8 点起飞」指的是出发机场当地的 8 点)。

    第二个设计是展示时的双标注。光按当地时区渲染还不够——用户在飞机上、在中转机场,他的「当前时间感」和事件发生地可能不同。所以在跨时区场景下同时标注两个时间:「当地 08:00(你所在地 14:00)」。这一条直接消掉了歧义,而它只是一个展示层的改动。

    第三个是提醒必须基于绝对时刻调度不能用「本地时间到点」触发,因为设备时区会变。用绝对时刻调度,无论用户在哪,提醒都在正确的瞬间响

    第四个是通知权限的兜底。这是很容易被忽略但影响很大的一环:用户没开通知权限,我们的所有出行提醒都是静默失效的——而我们以为已经提醒过他了。而出行提醒的失效后果可能是误机。

    所以要做两件事:在应用内也做醒目提醒(用户打开应用时明确展示「2 小时后的航班」);在关键行程前主动引导开启通知权限,并说明理由(「开启后我们会在登机前提醒你」)。

    第五个是地理围栏提醒(比如临近酒店时提示可以办理入住)。这类提醒体验很好,但地理围栏是持续耗电的。所以要按行程阶段动态开关:只在接近相关时间窗口时才注册围栏,过了就注销;同时限制同时注册的围栏数量(各端都有上限)。

    整体链路
    跨时区是旅游业务里最容易出错、后果最严重的一类 └─ 算错时间就是误机 第一个陷阱是语言上的歧义 ├─ 「明天早上 8 点的航班」是出发地的 8 点还是目的地的 8 点 └─ 界面上只写「08:00」,用户会用自己所在地的时间理解它 第一版的基础错误:以本地时间字符串存储与展示 │ ├─ 问题一:随设备时区漂移 │ 出发地看是 8 点 │ 飞到目的地设备时区变了,同一条数据渲染成另一个时间 │ 而航班时间压根没变 → 用户以为航班改时间了 │ └─ 问题二:提醒错时 提醒按「本地时间到点」触发 跨时区后提醒在错误的时刻响 可能提前几小时,也可能已经过了 一、时间一律以「绝对时刻 + 时区标识」存储 ├─ 绝对时刻保证唯一性 │ 不管设备在哪个时区,它指的都是同一个瞬间 └─ 时区标识用于正确展示 按事件发生地的时区渲染 因为「航班 8 点起飞」指的是出发机场当地的 8 点 二、展示时双标注(消掉歧义,只是展示层改动) ├─ 光按当地时区渲染还不够 │ 用户在飞机上、在中转机场 │ 他的「当前时间感」和事件发生地可能不同 └─ 跨时区场景同时标注两个时间 「当地 08:00(你所在地 14:00)」 三、提醒基于绝对时刻调度 ├─ 不能用「本地时间到点」触发,因为设备时区会变 └─ 用绝对时刻调度,无论用户在哪都在正确的瞬间响 四、通知权限的兜底(容易忽略但影响很大) │ ├─ 用户没开通知权限 → 所有出行提醒静默失效 │ 而我们以为已经提醒过他了 │ → 而出行提醒的失效后果可能是误机 │ ├─ 应用内也做醒目提醒 │ 打开应用时明确展示「2 小时后的航班」 │ └─ 关键行程前主动引导开启通知权限并说明理由 「开启后我们会在登机前提醒你」 五、地理围栏提醒(体验好但耗电) ├─ 场景:临近酒店时提示可以办理入住 ├─ 地理围栏是持续耗电的 ├─ 按行程阶段动态开关 │ 只在接近相关时间窗口时注册,过了就注销 └─ 限制同时注册的围栏数量(各端都有上限) 提醒的分级 ├─ 强提醒:航班、火车这类不可错过的(多次提醒) ├─ 弱提醒:入住、门票这类有弹性的(一次提醒) └─ 提醒时间要可配,不同用户的习惯差别很大 夏令时(容易被完全忽略) ├─ 部分地区有夏令时,切换日的时间计算要按时区库处理 └─ 不要自己算偏移,一定用标准时区数据
    分步拆解
    1. 先识别出「明天早上 8 点的航班」这句话本身是有歧义的。出发地的 8 点还是目的地的 8 点——如果界面上只写「08:00」,用户会用自己所在地的时间去理解
    2. 时间一律以「绝对时刻 + 时区标识」存储,绝不用本地时间字符串。这是所有问题的根源。
    3. 绝对时刻保证唯一性,时区标识用于正确展示。两者都要存——只有绝对时刻就不知道该按哪个时区渲染
    4. 展示按事件发生地的当地时区渲染。因为「航班 8 点起飞」指的是出发机场当地的 8 点,不是用户当前所在地的。
    5. 跨时区场景要双标注:「当地 08:00(你所在地 14:00)」。这一条直接消掉歧义,而它只是展示层的改动——性价比极高。
    6. 提醒必须基于绝对时刻调度,不能用「本地时间到点」触发。设备时区会变,按本地时间调度的提醒在跨时区后会在错误的时刻响
    7. 处理通知权限未开启的情况。没开权限时所有出行提醒都是静默失效的,而我们以为已经提醒过他了——后果可能是误机。
    8. 在应用内也做醒目提醒。用户打开应用时明确展示「2 小时后的航班」,不要只依赖系统通知
    9. 关键行程前主动引导开启通知权限,并说明理由。「开启后我们会在登机前提醒你」——说明理由的引导通过率明显更高
    10. 地理围栏提醒要按行程阶段动态开关。地理围栏是持续耗电的,只在接近相关时间窗口时注册、过了就注销。
    11. 限制同时注册的地理围栏数量。各端都有上限,超了会静默失败(注册不上但不报错)。
    12. 提醒要分级:强提醒(航班火车,多次提醒)与弱提醒(入住门票,一次提醒)。依据是「错过的后果有多严重」
    13. 提醒时间要可配。不同用户的习惯差别很大——有人要提前三小时,有人觉得一小时就够。
    14. 处理夏令时,而且一定要用标准时区数据不要自己算偏移。部分地区有夏令时,切换日的时间计算必须按时区库处理
    15. 设备时区变化时要能感知并刷新展示。用户落地开机后时区变了,界面上的双标注要跟着更新
    关键决策与取舍

    时间存「绝对时刻 + 时区标识」而不是只存绝对时刻。只存绝对时刻(时间戳)看起来更干净,但展示时不知道该按哪个时区渲染——按用户当前时区渲染是错的(航班时间指的是机场当地时间),而「事件发生地的时区」这个信息只能显式存下来代价是数据结构复杂一点、而且要保证时区标识的准确性(机场所在城市的时区要有可靠的数据源)。这个决策的一般形式是:时间的语义不只是「哪一瞬间」,还包括「按谁的时钟表达」,两者都要存。

    双标注是这个模块性价比最高的改动。它不改任何存储和调度逻辑,只是在展示层多显示一个时间,但它直接消掉了「8 点是谁的 8 点」这个歧义取舍是界面上信息密度增加,缓解手段是只在跨时区场景才双标注(用户所在地与事件发生地时区不同时才显示第二个),同城的行程不加这个噪音我认为这类「改展示而不改逻辑」的解法值得主动去找——很多歧义类的问题不需要复杂的技术方案。

    通知权限缺失要做应用内兜底,而不是只在设置里提示。「去设置里开启通知」这类提示用户经常忽略。而出行提醒的失效后果是误机,不能接受静默失效。所以应用内也做醒目提醒——用户打开应用时就能看到「2 小时后的航班」。代价是它只在用户主动打开应用时才生效,覆盖不了「完全没打开」的情况,所以引导开启通知权限仍然要做,两者是叠加而不是替代判断依据是「这个提醒失效的后果有多严重」——后果严重的提醒必须有多条触达路径。

    踩过的坑:以本地时间字符串存储,用户跨时区后看到的时间变了。用户在出发地看是 8 点,飞到目的地后同一条数据渲染成了另一个时间,而航班时间压根没变;有用户来问「你们是不是改了我的航班时间」。修法是改为绝对时刻加时区标识存储教训是:任何跨时区场景下的时间都不能用本地时间字符串表达——它的含义依赖于「读它的人在哪个时区」,而这个上下文是不稳定的。这条我在本地生活的营业时段判定上也遇到过(「早餐 6:00-10:00」是商家当地还是服务器时区),是同一类问题的不同形态

    踩过的坑二:提醒按本地时间调度,跨时区后在错误的时刻响。用户飞到目的地后,登机提醒提前了几小时响,或者已经过了才响。修法是基于绝对时刻调度教训是:调度类逻辑必须用绝对时间,展示类逻辑才用本地时间——把两者混用是最常见的时区 bug 来源。

    踩过的坑三:通知权限没开,所有出行提醒静默失效。我们的提醒系统在后台正常运行、日志也显示「已发送」,但用户压根没收到——因为他没开通知权限而我们以为已经提醒过他了,这是最危险的部分。修法是应用内兜底提醒加主动引导开启权限教训是:依赖系统能力的功能必须检查该能力是否可用,并且「不可用」要被当成一种需要处理的状态而不是忽略——权限、后台运行、网络,都属于这类。

    踩过的坑四:地理围栏注册数超上限,后面的围栏静默注册失败。我们给行程里的每个项目都注册了围栏,一个多日行程有十几个项目,超过了平台上限,后面的注册失败而且没有报错,表现是「有些地点的提醒从来不响」。修法是按行程阶段动态注册(只注册接近时间窗口的)并限制数量教训是:有配额上限的系统能力,注册失败往往是静默的,必须主动检查注册结果——这和「storage 写超限静默失败」是同一类问题。

    没做的部分:没做航班实时动态的主动推送(延误、登机口变更)。这需要接航司数据源并处理推送时效,属于服务端能力,端上只做了展示。也没做基于用户位置的智能提醒时间调整(离机场远就早点提醒),需要路况数据,当时提醒时间是固定可配的。

    数字是怎么测的

    时区正确性:确定性验证,而且要模拟设备时区变更——在出发地时区看行程、切换设备时区到目的地、检查事件时间不变、双标注正确更新这类用例必须常驻,因为它在单一时区的开发环境里测不出来。

    夏令时:确定性验证——构造夏令时切换日附近的行程,检查时间计算正确这一项要专门做,而且要说明用的是标准时区数据而不是自己算偏移

    提醒的准时性:提醒实际触发时刻与预期时刻的偏差,并专门验证跨时区场景(设备时区变更后提醒仍在正确瞬间触发)。

    通知权限的覆盖:通知权限开启率,以及引导开启后的转化率更重要的是报「未开启权限的用户中,通过应用内提醒看到行程提示的比例」——这个数字说明兜底路径的实际覆盖。

    时间歧义类反馈:「航班时间是不是改了」这类咨询的数量,改造前后对比。这是双标注最直接的业务收益,而且工单数可核查。

    地理围栏:围栏注册成功率(改造前超上限后静默失败)与同时注册的围栏数量上限。同时要报耗电对比——动态开关前后挂机一段时间的电量差,并说明机型

    提醒的实际效果:如果能拿到,报提醒触达后用户打开应用的比例。这说明提醒是有效的而不是被忽略的。

    不要报「提醒 100% 准时到达」。系统通知的到达受系统策略、省电模式、权限影响,端上无法保证正确表述是「时区正确性与夏令时由常驻用例保证、提醒基于绝对时刻调度、通知权限缺失有应用内兜底及其覆盖比例、围栏注册成功率与耗电对比」。

    面试追问
    Q:跨时区的行程时间怎么处理? A:时间一律以「绝对时刻 + 时区标识」存储,而且展示要双标注——因为「明天早上 8 点的航班」这句话本身就有歧义。第一版犯了基础错误:以本地时间字符串存储与展示(「2024-06-01 08:00」)。两个后果:随设备时区漂移——用户在出发地看是 8 点,飞到目的地后同一条数据渲染成了另一个时间,而航班时间压根没变,有用户来问「你们是不是改了我的航班时间」;提醒错时——按本地时间到点触发的提醒,跨时区后可能提前几小时响或者已经过了才响为什么不能只存绝对时刻(时间戳):那样展示时不知道该按哪个时区渲染——按用户当前时区是错的(「航班 8 点起飞」指的是出发机场当地时间),而「事件发生地的时区」只能显式存下来。一般形式是:时间的语义不只是「哪一瞬间」,还包括「按谁的时钟表达」,两者都要存。教训我在本地生活的营业时段判定上也遇到过(「早餐 6:00-10:00」是商家当地还是服务器时区),是同一类问题的不同形态。
    Q:用户怎么知道显示的 8 点是哪里的 8 点? A:跨时区场景双标注:「当地 08:00(你所在地 14:00)」——这是这个模块性价比最高的改动。不改任何存储和调度逻辑,只是在展示层多显示一个时间,但直接消掉了「8 点是谁的 8 点」这个歧义为什么按当地时区渲染还不够:用户可能在飞机上、在中转机场,他的「当前时间感」和事件发生地不同,所以要把两个时间都摆出来。取舍是界面信息密度增加缓解手段是只在跨时区场景才双标注(用户所在地与事件发生地时区不同时才显示第二个),同城行程不加这个噪音我想强调的是这类「改展示而不改逻辑」的解法值得主动去找——很多歧义类问题不需要复杂的技术方案。配套还要处理设备时区变化的感知:用户落地开机后时区变了,界面上的双标注要跟着更新度量上我会报「『航班时间是不是改了』这类咨询的数量」改造前后对比——这是双标注最直接的业务收益,而且工单数可核查。
    Q:出行提醒怎么调度?用户没开通知权限怎么办? A:调度必须基于绝对时刻,而通知权限未开启这件事我们踩过很危险的坑。先说调度:不能用「本地时间到点」触发,因为设备时区会变;用绝对时刻调度,无论用户在哪都在正确的瞬间响。教训是:调度类逻辑必须用绝对时间,展示类逻辑才用本地时间——把两者混用是最常见的时区 bug 来源。权限的坑是:我们的提醒系统在后台正常运行、日志也显示「已发送」,但用户压根没收到——因为他没开通知权限而我们以为已经提醒过他了,这是最危险的部分,因为出行提醒失效的后果可能是误机。修法两条叠加:应用内也做醒目提醒(用户打开应用时明确展示「2 小时后的航班」);关键行程前主动引导开启通知权限并说明理由(「开启后我们会在登机前提醒你」——说明理由的引导通过率明显更高)。两者是叠加而不是替代,因为应用内提醒只在用户主动打开时生效。教训是:依赖系统能力的功能必须检查该能力是否可用,并且「不可用」要被当成一种需要处理的状态而不是忽略——权限、后台运行、网络都属于这类。
    Q:临近酒店时提醒办入住,这种地理提醒怎么做? A:用地理围栏,但必须按行程阶段动态开关并限制数量——因为围栏是持续耗电的,而且有配额上限。我们踩过配额的坑:给行程里每个项目都注册了围栏,一个多日行程有十几个项目,超过了平台上限,后面的注册失败而且没有报错,表现是「有些地点的提醒从来不响」,查了很久。修法是只在接近相关时间窗口时注册围栏、过了就注销并限制同时注册的数量、而且要主动检查注册结果教训是:有配额上限的系统能力,注册失败往往是静默的,必须主动检查注册结果——这和我在私信里踩的「storage 写超限静默失败」是同一类问题。动态开关同时解决了耗电问题:地理围栏持续监听位置很耗电,而大部分时间是不需要的(用户还在别的城市)。另外提醒本身要分级强提醒(航班、火车这类不可错过的,多次提醒)和弱提醒(入住、门票这类有弹性的,一次提醒),依据是「错过的后果有多严重」而且提醒时间要可配——不同用户习惯差别很大,有人要提前三小时,有人觉得一小时就够。

模块三:凭证的线下出示

  1. 凭证的线下出示(离线可渲染的码内容 + 出示态屏幕亮度与常亮 + 动态码的时效与离线兜底 + 一屏聚合必要信息 + 出示失败的备用路径)★★★
    简历这样写 出行凭证的线下出示设计(码内容本地保存并端上离线渲染而非依赖图片下载 + 进入出示态自动提升屏幕亮度并保持常亮 + 动态码在离线时降级为长效静态码并标注 + 一屏聚合姓名与订单号与联系电话等必要信息 + 扫码失败时提供订单号与人工核验的备用路径):凭证出示是整个链路上失败代价最高的一刻(用户站在前台或闸机前,身后有人排队),早期凭证为服务端生成的图片需联网下载,无网时无法出示,且未处理屏幕亮度导致低亮度或强光下扫码识别困难;因此把码内容(而非图片)本地保存并在端上离线渲染,进入出示态时自动提升屏幕亮度并保持常亮、退出后恢复;对动态码在无网时降级为长效静态码并明确标注,避免因刷新失败而完全无法出示;出示页一屏聚合姓名、订单号、联系电话、地址等信息(扫码失败时工作人员通常改为手工核验这些字段);并提供扫码失败的备用路径(展示订单号、复制、以及联系客服的离线可见电话)。改造后无网络下凭证仍可出示,强光与低亮度下的识别由亮度控制改善,扫码失败有明确的人工核验备用路径
    展开完整拆解
    为什么要这么设计

    凭证出示这一刻,是整条链路上失败代价最高的时刻用户站在酒店前台或者景区闸机前,身后可能有人排队,工作人员在等他。这时候任何一点不顺畅都会被放大成很强的负面体验。

    而第一版在这一刻的表现很差,问题有四层。

    第一是凭证是服务端生成的图片,需要联网下载。没网就打不开——而用户恰恰经常在没网的时候需要它(前面模块讲过这个处境)。

    第二是屏幕亮度。用户为了省电把亮度调得很低,或者在户外强光下扫码设备读不出来。工作人员会让他「调亮一点」,用户手忙脚乱地去拉控制中心——这个体验在排队的时候很难受。而且如果屏幕在这期间自动息屏,还要重新解锁重新打开。

    第三是动态码在无网时完全不可用。有些凭证是动态码(定期刷新以防截图盗用)。第一版的实现是「刷新失败就不显示」,于是无网时用户看到一个空白或者一个「加载失败」——而这时他最需要的是能出示任何一种有效凭证

    第四是扫码失败时没有退路。扫码设备故障、码磨损、系统对不上,这些都会发生而工作人员的常规做法是改为手工核验——查订单号、核对姓名。但第一版的出示页只有一个大二维码,订单号要退回上一页去找,用户在压力下翻页面找信息,体验极差。

    所以四个设计,全部围绕「这一刻不能失败」。

    第一是保存码内容而不是码图片,端上离线渲染。二维码的本质是一串内容加一套编码规则——只要把内容存下来,端上就能自己画出来,压根不需要网络。这个改动是这个模块最关键的一步,它把凭证从「需要网络的资源」变成了「本地可生成的东西」

    第二是进入出示态自动提升屏幕亮度并保持常亮,退出后恢复。「退出后恢复」很重要——不能把用户的亮度永久改掉,那是对用户设置的侵犯。

    第三是动态码在无网时降级为长效静态码并明确标注。产品上要接受这个降级(安全性略低但可用),并且标注出来让工作人员知道核心判断是:能出示一个安全性稍低的有效凭证,远好于出示不了任何东西。

    第四是一屏聚合必要信息加备用路径。出示页上除了码,还要有姓名、订单号、联系电话、地址——这些正是扫码失败时工作人员会问的。另外提供复制订单号离线可见的客服电话(客服电话必须离线可见,因为需要它的时候通常就是出问题的时候)。

    整体链路
    这一刻的特殊性 └─ 失败代价最高 用户站在酒店前台或景区闸机前 身后可能有人排队,工作人员在等他 → 任何一点不顺畅都会被放大成很强的负面体验 第一版的四层问题 │ ├─ 凭证是服务端生成的图片,需要联网下载 │ 没网就打不开 │ 而用户恰恰经常在没网时需要它 │ ├─ 屏幕亮度 │ 用户为省电把亮度调得很低,或在户外强光下 │ 扫码设备读不出来 │ 工作人员让他「调亮一点」,他手忙脚乱拉控制中心 │ → 排队时这个体验很难受 │ 而且屏幕自动息屏还要重新解锁重新打开 │ ├─ 动态码在无网时完全不可用 │ 有些凭证是动态码(定期刷新防截图盗用) │ 第一版是「刷新失败就不显示」 │ 无网时用户看到空白或「加载失败」 │ → 而这时他最需要的是能出示任何一种有效凭证 │ └─ 扫码失败时没有退路 扫码设备故障、码磨损、系统对不上,这些都会发生 工作人员的常规做法是改为手工核验(查订单号、核姓名) 但第一版出示页只有一个大二维码 订单号要退回上一页去找 → 用户在压力下翻页面找信息,体验极差 一、保存码内容而不是码图片,端上离线渲染 ├─ 二维码的本质是「一串内容 + 一套编码规则」 ├─ 只要把内容存下来,端上就能自己画出来 └─ 这是最关键的一步 它把凭证从「需要网络的资源」 变成了「本地可生成的东西」 二、出示态的屏幕控制 ├─ 进入出示态:自动提升屏幕亮度 + 保持常亮 ├─ 退出出示态:恢复原有亮度设置 └─ 「退出后恢复」很重要 不能把用户的亮度永久改掉 那是对用户设置的侵犯 三、动态码在无网时降级为长效静态码 ├─ 产品上接受这个降级(安全性略低但可用) ├─ 并且明确标注,让工作人员知道 └─ 核心判断 能出示一个安全性稍低的有效凭证 远好于出示不了任何东西 四、一屏聚合必要信息 + 备用路径 │ ├─ 除了码,还要有 │ 姓名(与证件一致的那个) │ 订单号 │ 联系电话 │ 地址(当地语言版本,给司机或工作人员看) │ → 这些正是扫码失败时工作人员会问的 │ ├─ 提供复制订单号 │ └─ 客服电话必须离线可见 因为需要它的时候通常就是出问题的时候 其他细节 ├─ 出示态要能横竖屏都清晰(有些闸机是横向扫) ├─ 码要足够大,不要被其他信息挤压 ├─ 出示态进入路径要短:首页或通知点一下就到 │ 不要让用户在前台层层点击找入口 └─ 出示后记录一次「已出示」,便于客诉时追溯
    分步拆解
    1. 先认识这一刻的特殊性:失败代价最高。用户站在前台或闸机前、身后有人排队、工作人员在等他,任何不顺畅都会被放大
    2. 保存码内容而不是码图片,端上离线渲染。二维码的本质是一串内容加一套编码规则存下内容端上就能自己画,压根不需要网络。
    3. 理解这个改动的性质:它把凭证从「需要网络的资源」变成了「本地可生成的东西」。这是这个模块最关键的一步。
    4. 进入出示态自动提升屏幕亮度。用户为省电调低亮度或在户外强光下,扫码设备读不出来
    5. 进入出示态保持屏幕常亮。自动息屏后要重新解锁重新打开,在排队时很难受
    6. 退出出示态要恢复原有亮度设置。不能把用户的亮度永久改掉,那是对用户设置的侵犯
    7. 动态码在无网时降级为长效静态码。第一版「刷新失败就不显示」,用户看到空白或加载失败——而这时他最需要的是能出示任何一种有效凭证
    8. 降级要明确标注,让工作人员知道这是备用凭证。不要伪装成正常的动态码。
    9. 一屏聚合必要信息:姓名、订单号、联系电话、地址。这些正是扫码失败时工作人员会问的
    10. 地址要有当地语言版本。给出租车司机或工作人员看,中文地址在境外没有用
    11. 提供复制订单号的能力。工作人员可能要输入到他们的系统里。
    12. 客服电话必须离线可见。需要它的时候通常就是出问题的时候,而那时很可能没网
    13. 出示态要横竖屏都清晰。有些闸机是横向扫的,竖屏码被压扁会影响识别。
    14. 码要足够大,不要被其他信息挤压。聚合信息不能挤占码的显示面积——码是主体,信息是备用
    15. 出示态的进入路径要短:首页或通知点一下就到。不要让用户在前台层层点击找入口。
    关键决策与取舍

    保存码内容而不是码图片,这是一个「换视角就解决了」的改动。把凭证当成「服务端生成的图片资源」,就必然需要下载、需要网络、需要缓存图片文件;而把它当成「一串内容 + 端上的渲染能力」,网络依赖就整个消失了代价是端上要引入二维码渲染能力(包体积增加一点),而且要保证渲染出的码与服务端一致(编码参数、纠错级别要对齐)。取舍毫无疑问值得——包体积的一点增加换来了「凭证永远可用」。我认为这类改动值得主动去找:当某个功能有难以消除的依赖时,先问「这个依赖是本质的还是实现方式带来的」。

    动态码在无网时降级为静态码,这是安全性与可用性的正面取舍。动态码的目的是防截图盗用,降级为长效静态码确实降低了安全性。但反过来看:不降级的结果是用户压根出示不了凭证,而他是真实的、已付款的客人核心判断是「能出示一个安全性稍低的有效凭证,远好于出示不了任何东西」。缓解手段是明确标注为备用凭证(让工作人员知道可以额外核对身份)、静态码的有效期仍然有上限并且记录使用情况便于事后审计这个取舍要和业务方一起做,因为它涉及风险承担。

    一屏聚合必要信息,依据是「观察工作人员在扫码失败时做什么」。这个设计不是从技术出发的,而是从「扫码失败之后现场会发生什么」倒推的:工作人员会问姓名、要订单号、可能要打电话核实。把这些信息和码放在同一屏,等于把「备用流程」也准备好了取舍是出示页信息更多、要注意不能挤占码的面积——码是主体,聚合信息是备用,视觉权重要分清。

    踩过的坑:凭证是需要下载的图片,用户在前台没网打不开。这是我们收到的情绪最强的一类反馈——用户站在前台,屏幕上转圈,身后有人排队。修法是改为保存码内容并在端上离线渲染教训是:面对「某个功能强依赖网络」的问题,先问这个依赖是本质的还是实现方式带来的——这里的网络依赖完全是「用图片承载码」这个实现方式带来的,换成内容加渲染就消失了

    踩过的坑二:没处理屏幕亮度,扫码经常读不出来。用户为省电把亮度调到很低,或者在户外强光下屏幕反光,扫码设备读不出来;工作人员让他调亮,他手忙脚乱去拉控制中心,而屏幕还可能自动息屏。修法是进入出示态自动提升亮度并保持常亮,退出后恢复教训是:涉及硬件交互的界面(扫码、拍照、NFC)要主动控制设备状态,不能假设用户的设备处于合适的状态——用户没有义务为了用你的功能先去改系统设置。

    踩过的坑三:动态码刷新失败就不显示,无网时凭证完全不可用。用户看到一个「加载失败」,而他手里明明有一张已付款的有效订单。修法是降级为长效静态码并标注教训是:安全机制的失败不应该导致功能完全不可用——要设计一条「安全性降低但仍可用」的路径,否则安全机制会在最需要功能的时候把功能关掉

    踩过的坑四:扫码失败后用户要退回上一页找订单号。扫码设备故障时工作人员改为手工核验,而出示页上只有一个大二维码,订单号在别的页面;用户在压力下翻页面找信息,而这时他往往还在纠结「是不是我的手机有问题」。修法是一屏聚合姓名、订单号、电话、地址并提供复制教训是:关键流程的界面设计要考虑「这一步失败之后现场会发生什么」,并把备用流程需要的东西也准备好——只设计成功路径的界面,在失败时会把用户推入慌乱。

    没做的部分:没做离线的凭证有效性自校验(端上验证签名以防伪造凭证)。技术上可行但需要端上持有验证密钥,密钥泄露的风险大于收益,最后依赖工作人员的扫码系统去校验。也没做把凭证加入系统卡包(钱包类应用),各端支持差异大而且需要额外的资质。

    数字是怎么测的

    离线出示:确定性验证,这个模块最该报的——断开全部网络,检查凭证码能正常渲染并可被扫码设备识别「可被识别」这一步必须真的用扫码设备测,只检查「码显示出来了」是不够的(端上渲染的编码参数可能和服务端不一致)。

    码的一致性:端上渲染的码与服务端生成的码在扫码设备上的识别一致性这是「改为端上渲染」带来的新风险,必须验——编码参数、纠错级别要对齐。

    扫码成功率:凭证被扫码设备一次识别成功的比例,改造前后对比(亮度控制上线前后)。这个数据要靠线下实测或客诉数据推断,端上拿不到扫码设备的结果,这个局限要诚实说明

    「打不开凭证」类反馈:相关工单数量,改造前后对比。这是这个模块最直接的业务指标

    亮度恢复:确定性验证——退出出示态后检查屏幕亮度恢复为用户原有设置。这一项容易漏,而漏了就是对用户设置的侵犯

    动态码降级:确定性验证——无网状态下打开动态码凭证,检查降级为静态码、可正常显示、且有明确标注。三项都要验。

    备用路径的使用:「复制订单号」的点击次数这个数字有双重意义:证明备用路径在被真实使用;如果它的使用率很高,说明扫码失败的频率不低,值得去查扫码链路本身

    不要报「凭证 100% 可出示」。设备没电、应用被卸载、本地数据被清理,这些情况仍会导致出示失败正确表述是「离线渲染与识别由实测用例保证、端上与服务端码的一致性验证方式、亮度控制与恢复、动态码的降级路径、备用核验信息的聚合与其使用数据」,并说明扫码成功率的度量局限(端上拿不到扫码设备的结果)

    面试追问
    Q:用户在酒店前台没网,凭证怎么出示? A:保存「码内容」而不是「码图片」,在端上离线渲染——这是一个换视角就解决了的问题。第一版把凭证当成服务端生成的图片资源,那就必然需要下载、需要网络、需要缓存图片文件;用户站在前台,屏幕上转圈,身后有人排队——这是我们收到的情绪最强的一类反馈而二维码的本质是「一串内容 + 一套编码规则」,只要把内容存下来,端上就能自己画出来,压根不需要网络这个改动把凭证从「需要网络的资源」变成了「本地可生成的东西」。代价是端上要引入二维码渲染能力(包体积增加一点),而且要保证渲染出的码与服务端一致(编码参数、纠错级别要对齐)——这是新引入的风险,必须真的用扫码设备去验,只检查「码显示出来了」是不够的教训是:面对「某个功能强依赖网络」的问题,先问这个依赖是本质的还是实现方式带来的——这里的网络依赖完全是「用图片承载码」这个实现方式带来的
    Q:扫码设备读不出来是什么原因? A:很多时候是屏幕亮度,而这一点我们最初完全没处理。用户为省电把亮度调到很低,或者在户外强光下屏幕反光,扫码设备读不出来;工作人员让他「调亮一点」,他手忙脚乱去拉控制中心,而屏幕还可能自动息屏、要重新解锁重新打开——排队时这个体验很难受。修法是进入出示态自动提升屏幕亮度并保持常亮退出出示态恢复原有亮度设置「退出后恢复」这一条容易漏,而漏了就是对用户设置的侵犯(把他的亮度永久改掉了),所以要有专门的用例。教训是:涉及硬件交互的界面(扫码、拍照、NFC)要主动控制设备状态,不能假设用户的设备处于合适的状态——用户没有义务为了用你的功能先去改系统设置另外还有两个物理细节出示态要横竖屏都清晰(有些闸机是横向扫的,竖屏码被压扁会影响识别);码要足够大、不要被其他信息挤压——码是主体,聚合的备用信息是次要,视觉权重要分清。
    Q:动态码需要联网刷新,无网时怎么办? A:降级为长效静态码并明确标注——这是安全性与可用性的正面取舍。第一版的实现是「刷新失败就不显示」,用户看到一个「加载失败」,而他手里明明有一张已付款的有效订单核心判断是:能出示一个安全性稍低的有效凭证,远好于出示不了任何东西。动态码的目的是防截图盗用,降级确实降低了安全性;但不降级的结果是真实的、已付款的客人压根进不去缓解手段三条明确标注为备用凭证(让工作人员知道可以额外核对身份)、静态码仍有有效期上限记录使用情况便于事后审计这个取舍要和业务方一起做,因为它涉及风险承担,不能技术自己定。教训的一般形式是:安全机制的失败不应该导致功能完全不可用——要设计一条「安全性降低但仍可用」的路径,否则安全机制会在最需要功能的时候把功能关掉这和我在审批引擎里「解析不到审批人要兜底到管理员而不是卡死」是同一个取向:在「静默不可用」和「显式降级」之间选后者。
    Q:扫码失败了怎么办? A:提供人工核验的备用路径,而这个设计是从「观察工作人员在扫码失败时做什么」倒推出来的。扫码设备故障、码磨损、系统对不上,这些都会发生工作人员的常规做法是改为手工核验——问姓名、要订单号、可能要打电话核实。而我们第一版的出示页只有一个大二维码,订单号在别的页面,用户在压力下翻页面找信息,而这时他往往还在纠结「是不是我的手机有问题」。修法是一屏聚合姓名、订单号、联系电话、地址地址要有当地语言版本,中文地址在境外没用),并提供复制订单号(工作人员可能要输入到他们的系统里),以及离线可见的客服电话——客服电话必须离线可见,因为需要它的时候通常就是出问题的时候教训是:关键流程的界面设计要考虑「这一步失败之后现场会发生什么」,并把备用流程需要的东西也准备好——只设计成功路径的界面,在失败时会把用户推入慌乱度量上我会看「复制订单号」的点击次数:它证明备用路径在被真实使用,而如果使用率很高,说明扫码失败的频率不低,值得去查扫码链路本身

没有匹配的内容,换个关键词试试。

项目拆解 · 行程助手与出行中(uni-app)· 共 3 个模块 · 数字均为示例,需替换成自己项目的真实数据