起因骰子、抓阄、随机数全没用我试过骰子试过抓阄试过在群里发「1川菜2火锅3日料大家回复」。都能用但解决不了多人场景里「我不想吃这个但没法开口说」的问题。随机数扔出来还是有人皱眉头然后大家又开始重新商量。说白了问题不是缺一个随机器是缺一个让每个人都参与进来的决策过程。所以我做了这个决定今天吃什么鸿蒙版的吃饭决策工具。核心想法是把「吃什么」变成有仪式感的小游戏。三种模式对应三种场景转盘模式从你收藏的餐厅里转一圈停在哪家去哪家。一个人懒得想的时候用这个出来了别犹豫出门就走。投票模式这个我觉得是最有意思的地方。不是让大家「选想吃什么」而是让每个人「划掉不想吃的」。我拉了四个同事做过一次非正式对比——同一组8家候选餐厅、同一批人先用「正向选择」你想吃哪家再用「划掉不想吃的」。正向选那轮卡了将近五分钟投票排除那轮大概三十秒。排除法在心理上就是比正向选择容易没有谁要承担「是我定的」这个责任感。不是受控实验但这个差距我觉得不是误差。三个人一起划划完剩几个候选再随机抽一个大家都参与了结果也没人能抱怨。筛选模式按距离、口味、价格区间过滤候选列表缩小范围再随机。预算100块以内、步行20分钟内、不想吃辣——三个条件一过滤候选从几十家缩到三五家再转一圈干脆。技术上怎么实现的用的是 ArkTS ArkUI数据层模型设计得比较直接餐厅记录包含菜系、标签、价格区间、距离、权重这些字段历史记录里存了当时用的模式、过滤条件、候选列表方便做排重。export interface Restaurant { id: string name: string cuisines: string[] tags: string[] price: RestaurantPrice distanceKm: number weight: number // 用于加权随机 favorite: boolean archived: boolean } export interface HistoryItem { id: string ts: string mode: Mode // random | wheel | vote resultRestaurantId: string candidates: string[] filters: HistoryFilters meal: Meal rating?: number // 1-5 星评分可选 } 随机选择加了「避免重复」逻辑把最近N家的权重砍到0剩下的按各自权重抽签。用起来差别很明显「怎么又是这家」基本消失了。 投票模式的平票逻辑花了点时间想清楚。平票时不是随机取第一个那样会永远偏向列表靠前的餐厅而是以各自的票数为权重再做一次加权随机 arkts function finishVote(session: VoteSession): string { const entries Object.entries(session.votes) let max -1, winners: string[] [] for (const [id, count] of entries) { if (count max) { max count; winners [id] } else if (count max) { winners.push(id) } } if (winners.length 1) return winners[0] // 平票票数相同时以票数为权重再抽一次 const weights winners.map(id session.votes[id]) const total weights.reduce((a, b) a b, 0) let acc 0, r Math.random() * total for (let i 0; i winners.length; i) { acc weights[i] if (r acc) return winners[i] } return winners[winners.length - 1] } 实际上平票概率不高但不处理会在极端情况下出现明显偏差写清楚心里踏实。 --- ## 开发过程里踩过的坑 转盘动画这块试了三个方案最后全删了重写。ArkUI 的 animateTo 配合 rotation 可以跑但系统默认 ease 曲线停得太硬没有「快转、慢慢收住」的感觉。最后手写了一段三次方缓动核心是这个公式easeOut(t) 1 - (1 - t)³用帧回调把当前旋转角度从起点按这条曲线推到终点停下来的感觉算是过了自己这关。下篇会专门写这段——从 animateTo 的坑到最终逐帧实现完整过一遍。 本地存储用的是 preferences JSON 序列化没有引入额外依赖离线完全可用。吃饭决策这个场景用户绝对不想等网络请求这个选择没后悔过。 有点可惜的地方鸿蒙的 geoLocationManager API 能跑通但结合餐厅库做自动更新距离这个功能在1.0里太重砍掉了现在距离是用户手动填的。后面补。 --- ## 现状 App 在1.2.0版本上架鸿蒙应用市场目前市场页面显示下载量在50这个档位。内测群18人实际每周打开用的大概11个基本都是我直接拉进来的。 鸿蒙市场对新应用的自然流量接近零这事儿没什么好绕的。这18个人里用投票模式最多的是有固定饭搭子的掏出手机划一圈比群里发消息问快得多。一个人用的基本只用转盘有种说不清楚的仪式感。 接下来想做的用餐后打1-5星权重根据评分自动调整自动计算距离多人投票各自在自己手机上操作实时同步结果。最后那个功能实现难度最大但用处也最大。先把单机打磨好再说联网的事。 --- ## 一点真实感受 ArkTS 上手本身不难有 TypeScript 基础的话语法层面基本没什么障碍。真正让我花时间的是 ArkUI 那套状态装饰器——State、Link、Observed 边界在哪子组件改了数据父组件什么时候会刷新、什么时候不会这些搞清楚大概用了一周多中间有两天觉得自己写的东西像在盲猜。 文档覆盖的场景还是偏少很多时候只能自己写 demo 试。 如果你也在做鸿蒙 App或者有类似的坑踩过评论区直接说我都看。