一切的起点,是手机备忘录里一份密密麻麻的黄金买卖记录。

朋友手机备忘录里记了两年多的黄金买卖,长这样:
有日期、有价格、有克数、有自己手算的盈亏,还有一堆看不懂的「抵3」「汇1」。
需求很朴素:做个手机 App 专门记这个。但要真做成,得先把三件事聊明白。
这是第一个分歧点。同样一批买卖,算法不同,数字能差出几千块。最终做了三种并存、可随时切换:
算法 | 逻辑 | 适合谁 |
|---|---|---|
移动加权平均 | 每次买入后重算持仓均价,卖出按当前均价算盈亏 | 长期持有、看整体成本 |
先进先出 FIFO | 卖出时按最早的买入批次配对 | 波段、想知道"这波赚了多少" |
手动对应批次 | 卖出时自己指定对应哪几笔买入 | 精细化核对 |
答案是不记。因为记录时手续费已经折算进金价了,再单列一个字段只会造成重复计算。需求里看起来"应该有"的功能,有时候恰恰不该做。
这是整个项目最有意思的部分。朋友做波段有自己的一套黑话:
比如「汇1」汇总出 45 克、均价 1097.57;「抵3」卖掉后盈利 300。这些是人家的真实工作流,不是通用的金融概念。App 要好用,就得把这个习惯做进去,而不是让人来适应软件。
需求里有两个硬约束:手机上要像 App 一样打开、数据不能上传。
原生 App 开发成本高、还要装包;纯网页又没法"添加到桌面"。PWA 正好卡在中间:
manifest.webmanifest + sw.js,安卓和 iOS 都能添加到主屏幕localStorage,没有服务器,天然不上传一笔交易长这样:
group 字段是整个「汇/抵」功能的支点——有了它,才能按组算均价、算波段盈亏、判断抵没抵平。
三种算法共用一套 lots(批次)模型,区别只在于卖出时怎么挑批次:
按品种各自维护 lots,所以「积存金」和「实物金」的盈亏互不干扰。
对每一组,算四件事:
再加两个实用提示:回本价(= 组均价),以及按当前金价算的浮亏/浮盈。
62 笔手工录入不现实,所以写了个解析器,把备忘录原文直接变成结构化数据:
解析完做了交叉验证——App 算出来的盈亏和朋友自己手算的逐条对上了:
批组 | 手算 | App 计算 |
|---|---|---|
抵0 | 150 | 150.00 |
抵3 | 300 | 300.00 |
抵5 | 397.4 | 397.40 |
汇1(45克) | 均价 1097 | 1097.57 |

这部分比实现本身值钱。
String.replace 把 $' 当成了特殊符号为了生成一个"双击就能打开"的单文件 HTML,需要把 CSS/JS 内联进去。最初这么写:
结果整页 JS 报错。原因是 JS 的 String.replace 里,$' 表示"匹配之后的所有内容"——脚本里恰好有这个字符组合,于是 </body></html> 被注入进了 <script> 里。
修法是永远用函数式替换:
最早 sw.js 用的是「缓存优先」+ 固定缓存名 gold-ledger-v1。改完代码重新部署,用户打开还是旧页面。
三处整改:
v2,fetch 策略改成在线优先、失败回退缓存activate 时给页面发消息,检测到新 SW 就自动重载一次(用 sessionStorage 防抖,避免刷新死循环)用户反馈"金价怎么可能只有个位数"。真机截图里那个输入框带着 type="number" 的上下箭头——小米浏览器对 type="number" 有兼容 bug,会限制位数或吞掉后续输入。
改成文本类型 + 数字键盘,问题解决:
小米自带浏览器把这个功能叫「添加到桌面」「添加书签 → 桌面快捷方式」,藏在「工具箱」里,名字和 Chrome 完全不一样。
加了主动安装提示条:捕获 beforeinstallprompt 事件显示一键安装按钮;不支持的浏览器则弹出针对红米的图文路径。
备忘录里有「抵4.5」这个组,但正则 ([汇抵])\s*(\d+) 只认整数,.5 被丢掉,抵4.5 的两笔并进了抵4。加上小数匹配才修好:
有些记录长这样——交易行没写组号,组号写在后面的「盈利」行里:
解法是延迟回填:解析时把缺组号的交易记进 pending,遇到「盈利…抵N」行再回填上去。
原逻辑是「id 已存在就跳过」。问题来了:在电脑上改了一笔,再同步到手机,改动丢了。
改成同 id 覆盖更新。但备忘录解析出的 id 是 memo_1、memo_2… 会互相覆盖,所以备忘录路径改用内容指纹去重(时间+买卖+价格+克数+品种),同一段粘两次不会翻倍,不同段也不会打架。
写自动化测试时用 resources: 'usable' + 真实线上 URL,结果 jsdom 把已部署的旧版 app.js 拉下来覆盖了本地内联代码,断言莫名其妙失败。
改成只用本地内联脚本后稳定了。测试环境不要联网拉被测对象。
部署的是不含任何数据的空壳,用户数据始终在设备本地。换手机迁移走这条路:
这里还有个意外:二维码最大容量约 2.9 KB,而 62 笔数据有 12 KB,装不下。所以做了降级——大数据自动切换成「复制文本」,走微信转发再粘贴,体验一样顺。

几个复盘:

最终交付:一个能添加到手机桌面的离线 App,62 笔真实数据(2025-12-22 至 2026-09-11),三种盈亏算法可切换,本地存储不上传。
附录:技术栈
gold_ledger_v1原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。