部分信息已脱敏。持有访问密钥可查看完整版本。
商用车改装专门店
商用车改装经销商官网重做与在线报价器
把 Wix 旧站重做为 SolidStart 访客端加 Nuxt CMS,并加上覆盖 114 个价格点的在线改装报价器,首屏从三秒以上压到一秒以内。
概要
一家丰田 HIACE / 日产 CARAVAN 商用车改装经销商的官网重做。旧站建在 Wix 上,首屏三秒以上,且没有任何在线报价能力。新站从设计到上线由我一人完成,首屏压到一秒以内。
选型分两端:访客端用 SolidStart 做服务端渲染,运营端用 Nuxt 做 CMS,两者都跑在 Cloudflare Workers 上,共享同一个 D1。整体是 Turborepo 单仓,报价逻辑抽成独立包给两端复用。
核心功能:新车改装报价模拟器
报价器覆盖 13 种官方车体规格与 23 个改装 plan,交叉出 114 个可报价价格点,每个价格点挂载各自适用的选装。49 个选装分 8 组,单选组与多选组语义不同,全部组合约 8.6×10¹⁰ 种。
配置全部存在 D1,由 CMS 表单维护。运营能改车型、价格、选装和适用范围,但看不到 JSON,也写不了规则表达式。需要新的关系类型时,由我在引擎里加一种,再配一套业务语言的表单。
架构设计
三个 Worker 共享同一个 D1。访客站以只读为主,写路径只有问询提交和「気になる」计数两条;CMS 全量读写;抓取 YouTube 与 Instagram 的 cron Worker 每六小时跑一次。
cron Worker 是唯一接触第三方 API 的进程,第三方凭据也只配在它身上,访客站和 CMS 都拿不到。抓取结果写成 D1 快照,失败时只记错误不覆盖旧数据,首页继续显示上一次成功的内容。
技术难点
级联失效与定点迭代
取消一个选装,会让依赖它的选装失效,而这些失效又可能带走下一层。规则引擎用 while-changed 循环反复扫描选中集合,直到一轮扫描不再产生变化才停下,单趟遍历会留下悬空的选中项。
引擎是零运行时依赖的纯函数包,不认识 DOM、框架和数据库,所以访客端和 CMS 预览共用同一份判断,也能直接单元测试。金额相关的逻辑一律照这个方式抽离。
传输层的字典化压缩
嵌套结构会把选装目录在每个价格点里各嵌一份,114 个价格点合计 2,837 条适用关系,同一份选项定义被反复序列化。服务端改发顶层字典加 id 引用,客户端一次性还原,引擎和界面都不用改。
保存时拦截依赖环
运营若配出 A 需要 B、B 又需要 A,两个选装在前台会永远选不上。CMS 在写库前用三色标记做深度优先搜索,发现环就拒绝保存,并把 id 路径翻译成选装名称提示运营。
同一道检查还拦另外两种死配置:依赖目标落在同一个单选组里,以及依赖目标不在该价格点的适用选装内。两种情况都会让选装在前台显示却永远点不动。
其他判断
- 报价结果不落库。分享链接只带价格点 id 和选装 id,服务端从 D1 重算并重新过一遍规则引擎,手改 URL 凑不出非法组合来抬高总额
- 条件不满足的选装保持可见并禁用,标注「要◯◯」「◯◯と併用不可」,让客户看得到车型之间的差异
- 见积书 PDF 在浏览器端生成,jsPDF 只在点击时动态引入,不进 SSR 产物和主 bundle
- 报价配置读多写少,服务端结果走边缘缓存,TTL 五分钟
- 涉及 D1 的测试跑在真实 workerd 里,对真实 migration 建库,不 mock 数据库

