附录:报价整理器截至第 12 章的完整实现
前面几章每次只贴改动的那一小段,方便盯着这一步在动哪里。跟着敲代码的话,读到这里手上的文件到底是什么样子,容易只靠脑子去叠加历次改动。这一页把第 2 章到第 12 章对 parse-quotes.js 的每一次改动收在一处,给出当前这份完整实现,也给出每一处改动的来处,方便回头核对进度,或者发现自己哪一步抄漏了。
| 章节 | 改动 | 为什么改 |
|---|---|---|
| 第 2 章 | 建立 parseQuotesWithTemplate:按逗号切分字段,两端 trim,缺单位或缺价格标记为「缺失」 | 把一句模糊的请求换成「输入 / 输出 / 规则」模板后的第一版实现 |
| 第 7 章 | 判定器循环下定型为 parseQuotes,字段读取方式不变 | 红灯先行加判定器循环跑通 |
| 第 9 章 | missingPrice 新增负价格判断 | 面对两份都全绿的修改,选了把负价格纳入判断这一份,拒绝了把断言改松的那一份 |
| 第 10 章 | 品名字段带逗号且被双引号包裹时,改用认引号的 splitCsvLine 替换 line.split(',') | Diff 审查时在自己修和引入一个 CSV 解析库之间选了前者,避免无必要的新依赖 |
| 第 11 章 | 跳过以「品名」开头的表头行,不计入报价 | 真实使用者把带表头的原始导出文件直接粘了进来 |
| 第 12 章 | 逻辑未变,为已有的 trim 补上一条验收条件 | 变异测试发现删掉 trim 也能存活,说明这条行为之前没有测试覆盖 |
当前完整实现
Section titled “当前完整实现”// 第 10 章新增:按引号识别一行里的逗号分隔字段,// 引号内的逗号不切分,用来处理品名本身带逗号、被双引号包裹的情况function splitCsvLine(line) { const cells = []; let current = ''; let inQuotes = false; for (const char of line) { if (char === '"') { inQuotes = !inQuotes; continue; } if (char === ',' && !inQuotes) { cells.push(current); current = ''; continue; } current += char; } cells.push(current); return cells;}
function parseQuotes(text) { const lines = text.trim().split('\n').filter(Boolean);
return lines .filter((line, index) => { // 第 11 章新增:第一行如果是表头,不计入报价 if (index === 0 && line.startsWith('品名')) return false; return true; }) .map((line) => { // 第 2 章就有:字段两端去空格;第 12 章补上了对应的验收条件 const [name, unit, price] = splitCsvLine(line).map((cell) => cell.trim());
const missingUnit = !unit; // 第 9 章新增:价格为负也算缺失,不只是空或者零 const missingPrice = price === '' || price === undefined || Number(price) === 0 || Number(price) < 0;
return { name, unit: missingUnit ? '缺失' : unit, price: missingPrice ? '缺失' : Number(price), missing: missingUnit || missingPrice, }; });}
export { parseQuotes };用这份代码核对进度
Section titled “用这份代码核对进度”跟着前面章节敲到这一步,把自己手上的 parse-quotes.js 和上面这份对照一遍。如果某一处改动缺了,回到演进表里对应那一章重新过一次;如果多了自己额外加的逻辑,只要判定器循环里的测试仍然全绿,就不用为了和书里一样而删掉。这份附录给的是一个可以核对的基准,不是唯一允许存在的版本。
这份实现目前还没有处理的情况,比如价格带货币符号、单位写成中英文混杂,留在第 3 章的记录表和第 13 章的上线检查单里,作为已知但还没补上的空白,不在这里悄悄假装已经解决。