作品集
VAN-CONVERSION-ESTIMATOR PUBLIC / 2026

部分信息已脱敏。持有访问密钥可查看完整版本。

商用车改装专门店

商用车改装经销商官网重做与在线报价器

把 Wix 旧站重做为 SolidStart 访客端加 Nuxt CMS,并加上覆盖 114 个价格点的在线改装报价器,首屏从三秒以上压到一秒以内。

概要

一家丰田 HIACE / 日产 CARAVAN 商用车改装经销商的官网重做。旧站建在 Wix 上,首屏三秒以上,且没有任何在线报价能力。新站从设计到上线由我一人完成,首屏压到一秒以内。

选型分两端:访客端用 SolidStart 做服务端渲染,运营端用 Nuxt 做 CMS,两者都跑在 Cloudflare Workers 上,共享同一个 D1。整体是 Turborepo 单仓,报价逻辑抽成独立包给两端复用。

代码规模 6 万行以上 TypeScript
自动化测试 1,000 个以上用例
首屏加载 3 秒以上 → 1 秒以内

核心功能:新车改装报价模拟器

报价器覆盖 13 种官方车体规格与 23 个改装 plan,交叉出 114 个可报价价格点,每个价格点挂载各自适用的选装。49 个选装分 8 组,单选组与多选组语义不同,全部组合约 8.6×10¹⁰ 种。

配置全部存在 D1,由 CMS 表单维护。运营能改车型、价格、选装和适用范围,但看不到 JSON,也写不了规则表达式。需要新的关系类型时,由我在引擎里加一种,再配一套业务语言的表单。

可报价价格点 110 个以上
选装适用关系 2,800 条以上
LIVE 可以直接操作
报价器交互演示:选车体规格,选驱动方式与燃料,选改装 plan,勾选选装,总额实时更新;条件不满足的选装保持可见并禁用,附上日文理由。

架构设计

三个 Worker 共享同一个 D1。访客站以只读为主,写路径只有问询提交和「気になる」计数两条;CMS 全量读写;抓取 YouTube 与 Instagram 的 cron Worker 每六小时跑一次。

cron Worker 是唯一接触第三方 API 的进程,第三方凭据也只配在它身上,访客站和 CMS 都拿不到。抓取结果写成 D1 快照,失败时只记错误不覆盖旧数据,首页继续显示上一次成功的内容。

三个 Worker 共享同一个 D1 的部署结构与数据流向
三个 Worker 共享同一个 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 数据库
访客端报价模拟器:分步收敛的选择流程与实时更新的金额面板
访客端报价模拟器:分步收敛的选择流程与实时更新的金额面板
CMS 选装配置界面:依赖关系以业务文案呈现,运营接触不到 JSON 与规则表达式
CMS 选装配置界面:依赖关系以业务文案呈现,运营接触不到 JSON 与规则表达式
上线站点