You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS返回Firestore响应写入Google Sheets失败排查

Google Sheets与Firebase比对后无法写入目标表的代码错误说明

你的代码存在6个核心问题,直接导致写入失败:

  • Sheets写入请求未带认证,也未等待异步执行:写入环节你重新初始化了Sheets客户端,没有传入之前完成授权的jwtClient,请求会直接因无权限被Google服务拒绝;同时spreadsheets.values.update()是异步方法,你没有加await,调用刚触发云函数就可能因主线程执行完毕被回收,请求根本发不出去。
  • 全链路异步操作未正确等待,Promise链断裂:map回调里的Firestore写入set()、两层集合查询get()全是未加await的异步操作,外层的Promise.all(generateDuplicities)只会等待回调的同步代码执行完成,内部的查询、写入逻辑还没跑完,函数就已经结束执行。
  • 写入Sheets的数据格式不符合API要求:Sheets的values接口要求传入的values是二维数组,外层数组对应所有写入行,内层每个子数组对应一行的单元格数据;你当前传入的是一维字符串数组,会直接触发参数格式错误。同时你循环匹配结果时不断往同一个数组追加内容,且固定写入grpr!A12位置,会出现数据互相覆盖的问题。
  • Firestore操作无错误捕获:所有Firestore读写操作都没有加异常捕获逻辑,一旦出现权限错误、参数错误,不会打印任何有效日志,根本无法定位问题。
  • 重复初始化Sheets客户端:代码开头已经初始化了绑定v4版本的sheets实例,写入环节不需要再次调用google.sheets({version: 'v4'})新建实例,多余的初始化反而容易漏传配置。
  • HTTP触发器未返回响应:整个函数处理逻辑走完后没有调用response.send()/response.end(),云函数会持续运行直到超时,甚至可能在超时前中断正在执行的写入操作。

修正后的可运行代码示例

import * as functions from 'firebase-functions'
import { google } from 'googleapis'
import { initializeApp } from 'firebase-admin/app'
const serviceAccount = require('../sheets_updater_service_account.json')
// 全局只初始化一次sheets客户端
const sheets = google.sheets({version: 'v4'})
import { getFirestore } from "firebase-admin/firestore"
initializeApp()
const firestore = getFirestore()


module.exports.readAndUpdateAdministrativeSheet = functions.https.onRequest(async (request, response) => {
    try {
        // =========================== 服务账号认证 ===================================
        const jwtClient = new google.auth.JWT({
            email: serviceAccount.client_email,
            key: serviceAccount.private_key,
            scopes: ['https://www.googleapis.com/auth/spreadsheets']
        })
        await jwtClient.authorize()

        // ================= 读取源Google Sheet数据 =========================
        const { data } = await sheets.spreadsheets.values.get({
            auth: jwtClient,
            spreadsheetId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            range: `Listagem de pagamento!A2:X6`,
        })

        if (!data.values) {
            response.status(200).send('无有效数据可处理')
            return
        }

        // 按Sheets要求的二维数组格式存储匹配结果,从A12行开始写入
        const outputRows: string[][] = []
        const writeStartRow = 12

        // ========= 逐行处理源表格数据 ================
        const generateDuplicities = data.values.map(async row => {
            const [idade, nome, cpf, cpf_x, numeroRequerimento, arbitramentoHonorários,
                valorArbitrado, valorDeferido, valorComplementar, status, resultado, codigoBanco,
                banco, agencia, conta, dataDoRequerimento, dataRequerimento, dataStatus,
                comarca, vara, ato, assistidos, email, telefone] = row
            
            // 等待Firestore写入完成再走后续逻辑
            await firestore.collection("Listagem de pagamento").doc(numeroRequerimento).set({
                idade, nome, cpf, cpf_x, numeroRequerimento, arbitramentoHonorários,
                valorArbitrado, valorDeferido, valorComplementar, status, resultado, codigoBanco,
                banco, agencia, conta, dataDoRequerimento, dataRequerimento, dataStatus, comarca, vara, ato,
                assistidos, email, telefone
            })

            // 第一层查询:匹配cpf对应的司法流程文档
            const parentQuerySnapshot = await firestore.collection("Processos judiciais").where("documentosDosautores", "==", cpf).get()
            
            for (const parentDoc of parentQuerySnapshot.docs) {
                // 第二层查询:匹配对应仲裁费的子集合文档
                const childQuerySnapshot = await parentDoc.ref.collection("fee-arbitrations - Base de Execução").where('arbitramentoDeHonoráriosBE', '==', arbitramentoHonorários).get()
                
                for (const childDoc of childQuerySnapshot.docs) {
                    functions.logger.log(`匹配到结果:仲裁费${arbitramentoHonorários},姓名${nome},流程号${childDoc.data().processoBE}`)
                    // 每条匹配结果作为一行,组装为符合要求的子数组
                    outputRows.push([
                        `arbitramentoHonorários: ${arbitramentoHonorários}`,
                        `nome: ${nome}`,
                        `processoBE: ${childDoc.data().processoBE}`
                    ])
                }
            }
        })

        // 等待所有行的查询、处理逻辑全部执行完成
        await Promise.all(generateDuplicities)

        // 批量写入目标表格,传入已授权的认证客户端
        if (outputRows.length > 0) {
            await sheets.spreadsheets.values.update({
                auth: jwtClient,
                spreadsheetId: 'YYYYYYYYYYYYYYYYY',
                range: `grpr!A${writeStartRow}`,
                valueInputOption: 'USER_ENTERED',
                resource: { values: outputRows },
            })
        }

        response.status(200).send(`处理完成,共写入${outputRows.length}条匹配结果`)
    } catch (error) {
        functions.logger.error('函数执行出错', error)
        response.status(500).send(`执行出错:${error}`)
    }
}) 

额外注意事项

  • 提前把目标表格的编辑权限开放给serviceAccount对应的客户端邮箱,否则就算认证通过也会报权限不足错误。
  • 如果匹配结果量大,不要在循环里反复调用Sheets写入接口,统一组装完数据批量写入,能避免触发API频率限制。

内容的提问来源于stack exchange,提问作者Rodrigo Fiad Pasini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 11:31:11