嗯来吧 · 首页模块规则说明
版本 v11.0 | 范围:底部任务单跑马灯、瀑布流固定槽位(今日最新 ↔ 新潮热门) | 读者:前端 / 后端 / 测试
阅读方式:每个模块分 ① 定义 ② 正常规则 ③ 异常规则 ④ 字段接口 ⑤ 埋点 ⑥ 验收用例;规则编号与《产品规则手册》一致(跑马灯 R-T01~R-T27,场景位 R-G03~R-G06)。
模块一 · 底部任务单跑马灯
1.1 定义与位置
首页底部 Tab 栏上方的通条轮播(高 44px):📢 + 订单标题 + 【金额¥】+ 去接单 + ×。目的:把「可接的求助单」直接推到施助者面前。
1.2 正常规则 · 取数
| 场景 | 处理 |
|---|
| 数据来源 | 仅求助订单(不含技能 / 组队 / 活动 / 闲置) |
| 入池条件 | 状态为「招募中 / 待接单」、未被接单、未过期、审核通过 |
| 城市范围 | 按「采用城市」:手动选城 > 精确定位 > 城市级定位 > 省级(全省模式)> 默认上海 |
| 距离范围 | 同城 + 20km 内可接;省级(全省)模式不做半径过滤 |
| 条数 | 最多 5 条(marquee.maxItems 本期固定 5 条,见 1.2.2);不足按实际条数;0 条则隐藏 |
| 更多入口 | 跑马灯右侧「查看全部 ›」→ 求助大厅列表页(承接想看更多的需求) |
| 取数分层 | 近 24 小时发布优先 → 不足用近 3 天补齐 → 仍不足按实际条数(不用过期单凑数) |
1.2.1 入池条件(订单怎么进来)
| 维度 | 条件(全部满足才进池) |
| 类型 | 仅求助订单(技能 / 组队 / 活动 / 闲置不进池) |
| 状态 | 招募中 / 待接单;未接单、未取消、未下架 |
| 时间 | 未过期(订单有效期 > 当前时间) |
| 地域 | 属于「采用城市」:手动选城 > 定位城市 > 省级=全省 > 默认上海 |
| 距离 | 同城且 ≤ 20km(可配);省级模式不做半径过滤 |
| 审核 | 审核通过,无违规标记 |
| 发布者 | 信用分 > 50(信用差限流不进池);未被封禁 |
| 资金 | 赏金已托管成功(冻结失败的不展示) |
| 内容 | 标题 / 图片合规;命中敏感词不进池 |
| 控屏 | 同一发布者最多占 2 条;同一条订单 1 小时内最多曝光 3 轮 |
1.2.2 为什么默认 5 条(条数取值依据)
| 取值 | 后果 | 结论 |
| 1–2 条 | 像单条广告;大量可接订单得不到曝光;发布者投诉 | ❌ |
| 3–4 条 | 轮转更快,但覆盖率偏低(高峰池子常 20+ 条) | ⚠️ 池子小时按实际 |
| 5 条 | 一轮 25–30 秒(5 × 5–6 秒);用户可接受再次看到首条 | ✅ 默认 |
| 6–8 条 | 一轮 35–45 秒;第 6 条后曝光≈0,拖慢轮转 | ❌ 边际收益为负 |
| 100 条 | 轮播机制下用户永远看不到末尾条目,纯占位 | ❌ 无意义 |
取值依据:① 轮次时长 = 条数 × 平均停留,需控制在 20–30 秒;② 轮播位注意力集中在前 2 条,第 6 条起曝光接近 0;③ 校园同城 20km 未接单池通常十几到几十条,取前 5 即最优。
补充设计:跑马灯右侧提供「查看全部 ›」进入求助大厅列表页,满足想看更多的需求 —— 跑马灯做精选曝光,列表页做全量浏览。
可配置:marquee.maxItems 默认 5、范围 3–8;上线后依据一轮点击率与第 N 条点击率决定是否下调为 4。
1.3 正常规则 · 排序与轮播
| 项 | 规则 |
|---|
| 有定位(精确 / 城市级) | 距离升序 → 发布时间倒序 → 金额倒序 |
| 无定位 / 省级模式 | 金额倒序 → 发布时间倒序 |
| 主推条 | 排序第 1 条为「主推条」,停留 +2 秒 |
| 停留时长 | 基础(按字数)+ 加成,封顶 12 秒:≤10 字 4s / 11–15 字 5s / 16–20 字 6s / 21–25 字 7s / ≥26 字 8s;加成:本轮第 1 条 +2s、含打赏 +1s、综合金额 ≥500 元 +1s、已曝光 2 轮 −1s |
| 滚动方式 | 按排序顺序循环播放;每轮顺序固定(不随机) |
| 重排时机 | 每 15 分钟或下拉刷新重算一次 |
| 新单 | 下一轮生效;若金额高于当前首条可插入队首 |
| 疲劳控制 | 同一条订单 1 小时内最多曝光 3 轮,超出顺延下一条 |
1.3 排序规则(两层,定稿)
| 顺序 | 规则 |
|---|
| ① 距离档 | 有定位时:≤1km / 1–3km / 3–5 / 5–10 / 10–20km,先近档后远档;无定位或省级模式跳过本层 |
| ② 金额分 | 金额分 = min(综合金额, 500) ÷ 500,降序(综合金额 = 酬金(现金)+ 打赏豆数×0.1) |
| ③ 兜底 | 金额分相同 → orderId 升序(顺序稳定、可复现) |
| 急单兜底 | 距截止 ≤3 小时的订单,在同一距离档内提到最前,并显示「即将截止」标 |
| 无定位 / 省级 | 跳过距离档,直接按 金额分 DESC 排 |
不使用时效分 / 新鲜分(原设想的 1−已过÷总时长 与 1−发布小时÷72)。四条例由:① 两者方向相反会互相抵消;② 权重过低救不了急单(同档内"剩 23 小时"仍排在"剩 0.5 小时"前面);③ 时效分依赖订单有效期字段;④ 新鲜度问题已由"每天一批 5 条"的批次制解决。
1.3.2 额外打赏规则
| 项 | 规则 |
| 什么是打赏 | 酬金=现金 / 余额(可提现);打赏=用吧豆支付的追加鼓励(接单者获得吧豆,不可提现,只能换权益 / 排名) |
| 展示 | 分开显示:【酬金 80¥】【打赏 50 豆】 |
| 排序影响 | 综合金额 = 酬金 + 打赏豆数×0.1 → 金额分提高 → 同一距离档内更靠前;打赏只影响排序,不参与现金结算 |
| 约束 | ① 打赏不影响入池条件;② 单条订单吧豆折算部分≤50 元;③ 本期无加急、无付费置顶;④ 打赏档位与吧豆折算率待商业化确认 |
1.4 正常规则 · 交互
| 项 | 规则 |
|---|
| 点条目 | 进入该求助订单详情页 |
| 点「去接单」 | 进入详情页并聚焦报名区 |
| 点「×」消除 | 二次确认「今天不再显示?」→ 当天不再出现,次日 0 点恢复 |
| 消除记录 | 本地 + 服务端双写(换设备、清缓存仍生效) |
| 出现时机 | 进首页延迟 1.5 秒出现;页面下滑 >300px 隐藏,回到顶部再现 |
| 暂停 | 长按条目 / 页面不可见 / 请求中 → 暂停;恢复后从剩余时长继续 |
1.4.1 被接单后的补位机制(实时 or T+1)
| 链路 | 时效 | 说明 |
| 主链路:实时推送 | ≤ 30 秒 | 订单被接单 → 服务端推送「移除 + 补位」,客户端即时替换该条 |
| 降级链路:轮询 | 60 秒 | 无推送通道(WebSocket 断开/后台)时,每 60 秒拉一次增量 |
| 主动刷新 | 立即 | 用户下拉刷新 / 切换城市 / 回到前台 → 立即全量拉取 |
| 兜底重排 | T+1 + 每 15 分钟 | 每日 0 点全量重算;运行期每 15 分钟定时重排一次 |
补位细节:① 只前移不重排(保持顺序稳定,避免跳动);② 新单下一轮生效(金额高于首条可插队首);③ 补位后不足 5 条 → 按实际条数;④ 补位后为 0 条 → 隐藏跑马灯。
1.4.2 切换城市 / 省份时的刷新
| 步骤 | 行为 |
| 1 触发 | 用户在位置选择器选择新城市 / 全省 → 派发 locationChanged 事件 |
| 2 顶部 | 立即更新顶部文案(城市名 / 「省份 · 全省」);选城 ≠ 定位城市时提示「恢复定位」 |
| 3 跑马灯 | 立即清空并拉取新池(显示骨架 ≤300ms),不展示旧城市内容;新池为空则隐藏 |
| 4 场景位 | A(今日最新)、B(新潮热门)同时重算并按新城市替换 |
| 5 瀑布流 | 按新城市刷新列表;保持滚动位置或回到顶部(建议回到顶部) |
| 6 失败兜底 | 拉取失败 → 保留旧内容 + 顶部提示「切换失败,点击重试」(不静默失败) |
| 7 消除记录 | 消除标记按 「城市 + 日期」 维度存储:换城市后重新展示(不共用) |
特别说明(省级):用户只授权到省份(或 IP 定位)时,采用全省模式:城市字段显示「省份 · 全省」,订单池 = 全省可接求助单,距离显示「同省」,排序改用金额 → 计划完成时间,且不做 20km 半径过滤。
1.5 异常规则(研发必看)
| 场景 | 处理 |
|---|
| 无数据 | 整个跑马灯隐藏,不留白、不占位(瀑布流正常上移) |
| 只有 1 条 | 常显不轮播(避免"刚看到就切走") |
| 2–4 条 | 按实际条数循环;停留时长规则不变 |
| 订单被接单 | ≤30 秒内移除并补位;补位后不重新排序(防列表跳动) |
| 订单过期 | 同样移除并补位;已展示的曝光数据照常上报 |
| 网络失败 | 保留上次缓存内容继续轮播;仅首次无缓存时显示骨架屏,不显示错误页 |
| 定位权限被拒 | 按上海 + 金额排序;距离显示「同城」,不显示公里数 |
| 仅省级精度 | 按全省模式:金额排序、距离显示「同省」、不做半径过滤,并提示"选择城市可获得更准推荐" |
| 选城 ≠ 定位城市 | 按所选城市取数;顶部提示「当前按【X】展示 · 恢复定位」;距离以所选城市中心为基准并标注"按所选城市估算" |
| 切换城市中 | 保留旧内容,切换完成后一次性替换(不出现空白闪烁) |
| 当天已消除 | 不展示;次日 0 点恢复,以服务端时间为准(改客户端时间/跨时区无效) |
| 违规 / 审核中订单 | 不进池(状态过滤在服务端完成) |
| 页面内有弹层打开 | 暂停轮播计时,关闭后恢复 |
| 服务端时间与客户端不一致 | 一切判定以服务端 serverTime 为准 |
1.6 字段与接口
GET /home/marquee?city={城市}&precision={exact|city|province|none}&lng=&lat=
| 字段 | 说明 |
| items[].orderId | 订单 ID(点击跳转用) |
| items[].title | 标题(前端按 1 行省略) |
| items[].amount | 金额(整数,单位元) |
| items[].district | 区县(用于距离展示) |
| items[].distance | 距离(km,1 位小数);无定位时返回 null |
| items[].publishedAt | 发布时间(服务端时间戳) |
| scrollSec / firstExtraSec | 服务端可下发停留时长与主推条加时,便于运营调节 |
| hidden | 是否隐藏(无数据 = true) |
1.7 埋点
曝光(条数 / 采用城市 / 定位模式)、轮播次数与每条停留时长、点击条目、点击「去接单」、点「×」消除、手动暂停。
1.8 验收用例
| 场景 | 处理 |
|---|
| 正常 1 | 有定位、池中有 5 条 → 按距离升序轮播,每条按字数停 4–8s,首条 +2s |
| 正常 2 | 无定位 → 按金额降序,距离列显示「同城」,不显示公里数 |
| 正常 3 | 点「去接单」→ 详情页并聚焦报名区 |
| 正常 4 | 点「×」→ 二次确认 → 当天不再出现;次日 0 点恢复 |
| 异常 1 | 池中 0 条 → 跑马灯不显示,且页面无空白占位 |
| 异常 2 | 仅 1 条 → 常显不轮播 |
| 异常 3 | 某条被接单 → 30 秒内消失且后续条目前移,不重排 |
| 异常 4 | 断网 → 保留缓存继续轮播;恢复网络后内容刷新且不闪空 |
| 异常 5 | 拒绝定位权限 → 上海 + 金额排序 + 距离「同城」 |
| 异常 6 | 仅省级精度 → 全省模式 + 金额排序 + 距离「同省」+ 不做半径过滤 |
| 异常 7 | 选城与定位不同 → 按所选城市取数并显示"恢复定位"提示 |
| 异常 8 | 修改客户端时间 → 消除记录与恢复时间不受影响 |
模块二 · 瀑布流固定槽位(今日最新 ↔ 新潮热门)
2.1 定义与位置
瀑布流内同一个固定槽位(不新增格子、卡片高度固定),在 A = 今日最新 与 B = 新潮热门 两个状态间轮换。卡片 = 封面图 + 标题(2 行)+ 价格/单位 + 发布者信用 + 距离;右上角 2 个圆点表示当前态,顶部细进度条表示剩余时间。
2.2 正常规则 · 取数
| 项 | 规则 |
|---|
| A 今日最新 · 时间窗 | 当日 00:00–24:00 发布的内容 |
| A 数量与入口 | 显示 3 条(不足按实际),卡片底部「查看更多 ›」进入今日最新列表页 |
| A 排序 | 有定位 → 距离升序;无定位 → 发布时间升序;未授权定位 → 默认上海;两者都取前 3 条 |
| A 排除 | 审核中、已下架、已被接单(订单类) |
| B 新潮热门 · 时间窗 | 近 7 天 |
| B 数量与入口 | 同样显示 3 条+「查看更多 ›」(两态结构对称,卡片高度一致) |
| B 热度分 | 浏览 ×0.4 + 收藏 ×0.3 + 成交 ×0.3;新奇技能 ×1.3;同分按发布时间倒序 |
| B 刷新 | 每 10 分钟重算一次 |
| 去重 | A 与 B 不出现同一条;B 若与 A 重复则顺延取下一条 |
2.3 正常规则 · 轮换
| 项 | 规则 |
|---|
| 轮换方式 | A → B → A → B 交替(顺序固定,不随机) |
| 停留时长 | 每个状态 8 秒(后台可配 5–15s) |
| 首次进入 | 默认显示 A(今日最新);重新进入首页都重置为 A |
| 手动切换 | 左右滑卡片 或 点击圆点 → 立即切换,并暂停自动轮询 3 个周期(24 秒) |
| 暂停条件 | 页面不可见、槽位滚出可视区、请求数据中、从详情页返回 2 秒内、长按卡片 |
| 恢复 | 从剩余时长继续,不重新计时 |
| 预加载 | 切换前 1 秒预取下一状态数据与首图;首屏 A、B 一次请求返回 |
| 动画 | 横向滑动 300ms;低端机降级淡入 200ms;卡片高度固定,切换不引起瀑布流跳动 |
2.4 异常规则(研发必看)
| 场景 | 处理 |
|---|
| A 无数据 | 只显示 B 且停止轮询(不显示空卡片) |
| A 只有 1–2 条 | 按实际条数显示;「查看更多」入口保留 |
| 查看更多页为空 | 显示空态「今天还没有新内容」+「去发布」按钮,不显示空白页 |
| 两态条数不一致 | 按较少的一侧补齐占位或统一取 3 条,保证卡片高度一致(防瀑布流跳动) |
| B 无数据 | 只显示 A 且停止轮询 |
| A、B 都无 | 隐藏整个槽位,瀑布流自动上移,不留白、不占位 |
| A 与 B 返回同一条 | B 自动顺延取第 2 条,避免"切了等于没切" |
| B 数据不足 6 条 | B 常显 24 秒后回到 A,并把轮询间隔放宽到 15 秒 |
| 网络失败 | 保留上次缓存内容继续轮换;仅首次失败显示骨架屏,不显示错误页 |
| 图片加载失败 | 显示占位图;不影响标题、价格与点击 |
| 用户快速连续手动切换 | 防抖 300ms,以最后一次点击为目标状态 |
| 切换城市 / 定位变化 | 立即重算 A 与 B,整体替换内容并保持卡片高度 |
| 从后台回前台 | 恢复计时(按剩余时长),并检查是否已超过刷新周期,超过则先刷新再继续 |
| 内容被下架 / 删除 | 从当前轮换中移除并补位;若 A 或 B 因此为空,按"无数据"处理 |
2.5 字段与接口
GET /home/scene-slot?city={城市}&precision={exact|city|province|none}&lng=&lat= → 返回 { today:[…], hot:[…], durationSec:8 }
| 字段 | 说明 |
| today[].itemId / title / cover / price / unit | A 态内容(当日发布),最多返回 3 条 |
| todayTotal / hasMore | 当日总数与是否还有更多(决定「查看更多」是否可点) |
| today[].distance | 距离(km);无定位返回 null,前端显示「同城」 |
| hot[].itemId / title / cover / price | B 态内容(近 7 天热度),最多返回 3 条 |
| hotTotal / hasMore | 热门总数与是否还有更多 |
| hot[].heatScore | 热度分(用于排查排序,前端不展示) |
| hot[].isNovel | 是否新奇技能(展示「新奇」角标) |
| durationSec / disableRotate | 停留时长、是否停止轮询(单侧为空时由服务端下发) |
2.6 埋点
曝光(scene=A/B、itemId、停留时长、是否自动)、点击、手动切换、空态次数、切换城市后的内容刷新成功率。
2.7 验收用例
| 场景 | 处理 |
|---|
| 正常 1 | A、B 各停 8 秒交替;进度条走完即切换,卡片高度不变 |
| 正常 2 | 手动切换后 24 秒内不再自动切换;随后恢复交替 |
| 正常 3 | A 有定位按距离最近;无定位按当日最早发布 |
| 正常 4 | B 按热度分降序,新奇技能带角标且加权 |
| 正常 5 | 今日最新显示 3 条,底部「查看更多」可点,进入列表页后可上滑分页加载 |
| 正常 6 | 今日最新不足 3 条(1–2 条)时按实际显示且「查看更多」仍在 |
| 异常 1 | A 为空 → 只显示 B 且停止轮询 |
| 异常 2 | B 为空 → 只显示 A 且停止轮询 |
| 异常 3 | A、B 都空 → 槽位隐藏且无空白占位 |
| 异常 4 | A、B 返回同一条 → B 自动顺延,两次展示内容不同 |
| 异常 5 | B 仅 3 条 → 常显 24 秒后回 A,轮询间隔放宽到 15 秒 |
| 异常 6 | 断网 → 显示缓存内容;恢复后刷新且不闪空 |
| 异常 7 | 切换城市 → A、B 立即重算并整体替换,页面不跳动 |
| 异常 8 | 切后台 5 分钟回前台 → 从剩余时长继续并先刷新数据 |
| 异常 9 | 两态条数不同时卡片高度不变,瀑布流不跳动 |
| 异常 10 | 查看更多页无内容 → 显示空态与「去发布」,不是空白页 |