跳转到内容

附录:报价整理器截至第 12 章的完整实现

约 10 分钟 难度:新手 理解章

前面几章每次只贴改动的那一小段,方便盯着这一步在动哪里。跟着敲代码的话,读到这里手上的文件到底是什么样子,容易只靠脑子去叠加历次改动。这一页把第 2 章到第 12 章对 parse-quotes.js 的每一次改动收在一处,给出当前这份完整实现,也给出每一处改动的来处,方便回头核对进度,或者发现自己哪一步抄漏了。

章节改动为什么改
第 2 章建立 parseQuotesWithTemplate:按逗号切分字段,两端 trim,缺单位或缺价格标记为「缺失」把一句模糊的请求换成「输入 / 输出 / 规则」模板后的第一版实现
第 7 章判定器循环下定型为 parseQuotes,字段读取方式不变红灯先行加判定器循环跑通
第 9 章missingPrice 新增负价格判断面对两份都全绿的修改,选了把负价格纳入判断这一份,拒绝了把断言改松的那一份
第 10 章品名字段带逗号且被双引号包裹时,改用认引号的 splitCsvLine 替换 line.split(',')Diff 审查时在自己修和引入一个 CSV 解析库之间选了前者,避免无必要的新依赖
第 11 章跳过以「品名」开头的表头行,不计入报价真实使用者把带表头的原始导出文件直接粘了进来
第 12 章逻辑未变,为已有的 trim 补上一条验收条件变异测试发现删掉 trim 也能存活,说明这条行为之前没有测试覆盖
parse-quotes.js(截至第 12 章)
// 第 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 };

跟着前面章节敲到这一步,把自己手上的 parse-quotes.js 和上面这份对照一遍。如果某一处改动缺了,回到演进表里对应那一章重新过一次;如果多了自己额外加的逻辑,只要判定器循环里的测试仍然全绿,就不用为了和书里一样而删掉。这份附录给的是一个可以核对的基准,不是唯一允许存在的版本。

这份实现目前还没有处理的情况,比如价格带货币符号、单位写成中英文混杂,留在第 3 章的记录表和第 13 章的上线检查单里,作为已知但还没补上的空白,不在这里悄悄假装已经解决。

这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更