// 新文档:新建「文档共享脚本」,粘贴保存,再将 webhook 填到后台该活动的金山同步设置。 // 已连接的文档可原位替换此脚本并保存;不要更换原 webhook。这里不填写令牌。 const HEADERS = ["导出时间", "拼车号", "完整手机号", "微信名", "抽卡次数", "结果1", "结果2", "结果3", "结果4", "结果5", "结果6"]; const input = typeof Context === "undefined" ? null : Context.argv; const mode = input && input.mode; const sheetName = input && typeof input.sheet_name === "string" ? input.sheet_name : ""; const receipt = {receiver:"card-draw-results-v2", schema_version:2, mode:mode || "manual", sync_id:input && input.sync_id || "", sheet_name:sheetName, column_count:11, row_count:0}; function rejected(code) { return {...receipt, ok:false, code}; } if (!mode) { console.log("通用同步脚本已就绪,本次没有写入数据。请回后台选择活动,设置文档与工作表后检查连接。"); return {...receipt, ok:true}; } if (!input || input.schema_version !== 2 || !sheetName || sheetName.length > 31 || /[\[\]:*?\/\\\x00-\x1f]/.test(sheetName) || !input.sync_id || !["check", "replace"].includes(mode)) return rejected("INVALID_PAYLOAD"); let sheet = null; try { for (let i = 1; i <= Application.Sheets.Count; i++) { const current = Application.Sheets.Item(i); if (current.Name === sheetName) { sheet = current; break; } } } catch { return rejected("SHEET_NOT_FOUND"); } if (!sheet) return rejected("SHEET_NOT_FOUND"); if (mode === "check") return {...receipt, ok:true}; if (JSON.stringify(input.headers) !== JSON.stringify(HEADERS) || !Array.isArray(input.rows) || !input.rows.length || input.rows.length > 5000 || input.rows.some(row => !Array.isArray(row) || row.length !== 11 || row.some(cell => typeof cell !== "string" && typeof cell !== "number"))) return rejected("INVALID_PAYLOAD"); try { const used = sheet.UsedRange; const usedEnd = used ? Number(used.Row) + Number(used.Rows.Count) - 1 : 1; const lastRow = Math.max(1, usedEnd, input.rows.length + 1); if (!Number.isInteger(lastRow) || lastRow > 5001) return rejected("RANGE_TOO_LARGE"); const target = sheet.Range("A1:K" + lastRow); if (target.MergeCells !== false) return rejected("MERGED_CELLS"); // 文本格式保留拼车号、完整手机号及以等号开头的微信名;不执行单元格公式。 const values = [HEADERS, ...input.rows.map(row => row.map(cell => String(cell)))]; while (values.length < lastRow) values.push(Array(11).fill("")); target.NumberFormat = "@"; // 一次覆盖含旧数据尾行的矩阵,不先清空,也不删除整行或触碰 L 列以后的内容。 target.Value2 = values; const written = target.Value2; if (!Array.isArray(written) || written.length !== values.length || values.some((row, i) => !Array.isArray(written[i]) || row.some((cell, j) => String(written[i][j] == null ? "" : written[i][j]) !== cell))) return rejected("WRITE_FAILED"); return {...receipt, ok:true, row_count:input.rows.length}; } catch { // 不回传上游异常或单元格内容,不尝试解除工作表保护。 return rejected("WRITE_FAILED"); }