# Next.js 和 Flutter 的统一计算引擎:如何避免数字差异
当同一个建筑计算器同时以 Flutter 移动应用和 Next.js 网站的形式存在时,即使是公式中的细微差异,也会动摇用户信任。一个真实案例显示:如果应用和网站为栅栏柱输出不同的混凝土体积,这不仅仅是 bug——而是逻辑重复导致的系统性问题。解决方案——基于声明式 JSON 规范和一致性测试的单一真相源。
两个独立代码库的问题
最初,计算逻辑在 Dart(用于 Flutter 应用)和 TypeScript(用于 Next.js 网站)中分别实现了两次。两个项目并行但独立演进。系数、默认值(如柱孔直径)、舍入规则和材料包装信息分别存在于每个运行时的代码中。结果:相同输入产生不同的输出。
这种架构违反了 DRY(不要重复自己)原则,并积累了技术债,最终通过用户投诉显现。对于利基工具来说,这尤其关键,因为计算准确性直接影响产品信任。
声明式方法:JSON 作为真相源
与其重复业务逻辑,不如将所有变量参数移到结构化的 JSON 文件中——每个计算器一个规范。每个规范包含:
- 输入字段的默认值(例如,桩直径——150 mm)
- 舍入规则(始终向上取整)
- 取决于精度模式的损耗系数
- 材料包装信息(25 kg 袋,0.01 m³/袋产量)
- 最小层厚和紧固件间距
这些 JSON 文件成为单一真相源。现在,任何参数更改只需在 JSON 中进行一次,即自动应用于两个运行时。
代码生成实现同步
为确保两个项目使用相同数据,引入了代码生成流程:
- JSON 规范变更时,运行
npm run sync:specs脚本 - 脚本读取所有规范,生成包含常量映射(
const Map)的 Dart 文件 - 生成的文件提交到 Flutter 应用仓库
- 两个系统的 CI/CD 检查变更并部署更新版本
生成的文件标记为 @generated,linter 阻止手动编辑。这消除了人为错误,确保数据一致性。
一致性测试:防范逻辑差异
即使数据相同,语言差异也可能导致行为差异:TypeScript 中的 Math.ceil() 和 Dart 中的 num.ceil() 在边缘情况下可能不同。为防止此问题,实现了测试系统:
- 一组测试夹具——61 个计算器的数十种输入组合
- TypeScript 引擎运行所有夹具,将结果保存到参考文件
- Flutter 仓库使用同一文件,与 Dart 引擎结果比较
- 任何差异——即使是小数点后最后一位——都会导致 CI 失败
六个月内,这些测试捕获了三个真实差异,如果没有自动化检查,这些问题将无人察觉。
取代“加 10%”的三种精度模式
传统计算器常使用通用安全系数(例如 +10%)。这种捷径引入不准确:瓷砖需要裁切余量、腻子需要补偿基层不平、胶粘剂需要考虑表面吸水。
取而代之的是三种计算模式:
- 基础——基于 GOST/SP 标准的无余量计算
- 实际(默认)——考虑典型公寓装修条件
- 专业——复杂项目和缺货保障的最大余量
每种模式的系数也存储在 JSON 规范中,可无需重建应用即可更改。
关键规则:相同输入下,结果必须满足 基础 ≤ 实际 ≤ 专业。专属 CI 测试验证所有计算器。
验证器作为第二道防线
除了单元测试和一致性检查,CI 运行三种专用验证器:
- 逻辑验证器:确保购买量 ≥ 净量,有效输入不产生零结果,标签使用正确公制单位(“m²”,而非“sq m”)
- 包装验证器:确认现实材料包装(胶粘剂 5/10/15 L 桶,瓷砖成包而非单片)
- 单位验证器:捕获公制混淆(例如,未转换直接将毫米加到米上)
这些脚本如同系统关口的“安全检查站”——不取代单元测试,但防止愚蠢却关键的生态级错误。
关键要点
- 通过 JSON 规范的单一真相源 消除运行时参数重复。
- 代码生成 确保无手动干预的数据同步。
- 一致性测试 保证 Dart 和 TypeScript 结果相同。
- 三种精度模式 取代简化的“+10%”,让用户控制余量。
- CI 中的专用验证器 防范单元测试可能遗漏的系统错误。
— Editorial Team
暂无评论。