
本文介绍如何通过 google apps script 实现 qr 扫码后自动检查 google sheets 列中是否已存在该值,若重复则在 html 页面显示“already existing”,否则写入新数据,确保数据唯一性。
本文介绍如何通过 google apps script 实现 qr 扫码后自动检查 google sheets 列中是否已存在该值,若重复则在 html 页面显示“already existing”,否则写入新数据,确保数据唯一性。
在构建基于 Google Web App 的 QR 扫码系统时,仅比对前端缓存的 lastResult 无法保证数据唯一性——因为 Sheet 中可能早已存在相同条目,而用户多次扫描同一码(中间穿插其他码)时仍会重复写入。真正可靠的方案是:每次扫码后,由服务端(Apps Script)实时查询整个 Sheet 数据,确认值是否已存在,再决定写入或返回提示。
✅ 正确实现逻辑(前后端协同)
1. 前端 JavaScript:使用 withSuccessHandler 接收结构化响应
原代码中直接调用 google.script.run.sendback(decodedText) 是异步且无返回处理的,导致无法判断结果。必须配合 withSuccessHandler 捕获服务端返回的对象:
async function onScanSuccess(decodedText, decodedResult) {
// 封装为对象,便于传递和扩展
const payload = { text: decodedText };
google.script.run
.withSuccessHandler((response) => {
if (response.result === "fail") {
$('#qr-reader-results').prepend('<p style="color:red;">⚠ Already existing.</p>');
console.log('Duplicate detected:', decodedText);
} else if (response.result === "ok") {
$('#qr-reader-results').prepend(`<p>✅ Scanned: ${decodedText}</p>`);
console.log('New entry added:', decodedText);
}
})
.sendback(payload); // 传入对象,非纯字符串
}⚠ 注意:
google.script.run默认不等待 Promise,withSuccessHandler是其标准回调机制,不可与await混用;onScanSuccess本身无需声明async(除非内部有其他 await 操作)。
2. 后端 Apps Script:高效查重 + 原子写入
关键改进点:
- 使用
getDataRange().getValues()获取全量数据(适用于千行内场景); - 遍历第一列(Column A)严格比对;
-
统一返回结构化对象(含
result和text),避免类型歧义; - 写入前确保
getLastRow()准确性(建议加空行校验,见下方优化提示)。
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
function sendback(payload) {
const searchText = payload.text?.toString().trim();
if (!searchText) {
return { result: "fail", text: searchText, reason: "Empty input" };
}
// 获取全部数据(含空行,但 getValues() 自动截断末尾空行)
const data = sheet.getDataRange().getValues();
// 查找 Column A(索引 0)中是否已存在该值(精确匹配,区分大小写)
for (let i = 0; i < data.length; i++) {
if (data[i][0] === searchText) {
return { result: "fail", text: searchText };
}
}
// 无重复 → 写入新行
const nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1).setValue(searchText);
return { result: "ok", text: searchText };
}3. 增强健壮性(推荐补充)
-
性能优化(大数据量):若 Sheet 行数 > 5000,改用
sheet.getRange("A:A").getValues()并.filter(r => r[0] !== "")缩小遍历范围; -
空行防护:
getLastRow()在首行为空时可能不准,可改用:const lastRow = Math.max(sheet.getLastRow(), 1); const nextRow = sheet.getRange("A" + lastRow).getValue() ? lastRow + 1 : lastRow; -
去空格/大小写归一化(按需):
if (String(data[i][0]).trim().toLowerCase() === searchText.trim().toLowerCase())
4. HTML 页面提示优化(用户体验)
在 <div id="qr-reader-results"></div> 中插入带样式的反馈,例如:
<p style="background:#f8f9fa; padding:8px; margin:4px 0; border-left:3px solid #28a745;"> ✅ Scanned: ABC123 </p> <p style="background:#f8d7da; padding:8px; margin:4px 0; border-left:3px solid #dc3545;"> ⚠ Already existing. </p>
? 总结
- ❌ 错误做法:仅前端比对
lastResult,或未处理google.script.run异步返回; - ✅ 正确路径:服务端查重 → 结构化返回 → 前端
withSuccessHandler分支处理; - ? 安全提示:本方案所有校验发生在服务端,不受前端篡改影响,保障数据一致性;
- ? 扩展方向:可进一步添加时间戳、用户ID、防抖限频等字段,构建完整扫码日志系统。
通过以上改造,你的 Web App 将真正实现「Sheet 级去重」,杜绝重复录入,同时提供清晰即时的用户反馈。


















