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

Google Calendar API JavaScript事件冲突查询报错排查求助

问题分析与修复方案

1. 403权限错误原因及修复

问题:表单提交时,isFreeDuringEvent函数在用户完成认证前就被调用,此时没有有效访问令牌,导致API请求被拒绝。

修复:调整流程,确保先完成用户认证,再执行冲突检查和事件创建操作。在表单提交处理器中,将冲突检查和事件创建逻辑移到认证回调内部,或先验证令牌有效性再执行后续操作。

2. 类型错误(Cannot read 'result' of undefined)原因及修复

问题:当API请求失败(如403错误)时,response变量未被赋值,后续代码直接访问response.result导致类型错误。

修复:在访问response.result前添加存在性检查,并为events设置默认空数组;同时在isFreeDuringEvent函数中捕获错误后返回明确的冲突状态。

3. 时区处理错误原因及修复

问题:查询事件时未指定时区,API将timeMin和timeMax解析为UTC时间,与用户事件使用的America/Los_Angeles时区不匹配,导致冲突检查结果不准确。

修复:在事件列表请求中添加timeZone参数,明确指定时区为America/Los_Angeles,确保时间范围解析正确。

4. 异步逻辑混合问题修复

问题:createEvent函数同时使用await和回调函数,导致逻辑混乱。

修复:统一使用async/await处理异步操作,移除冗余回调。


完整修正代码

<!DOCTYPE html>
<html>
<head>
  <title>Google Calendar Event Scheduling</title>
  <meta charset="utf-8" />
</head>
<body>
  <p>Google Calendar Event Scheduling</p>
  <button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
  <br>
  <button hidden id="event_button" onclick="handleEventClick()">Create Default Event</button>
  <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
  <br>
  <form name="event_form" id="event_form">
    <label for="event_summary">Event Summary:</label>
    <br>
    <input name='event_summary' id="event_summary" type="text">
    <br>
    <br>
    <label for="event_location">Event Location:</label>
    <br>
    <input name="event_location" id="event_location" type="text">
    <br>
    <br>
    <label for="event_description">Event Description:</label>
    <br>
    <textarea name="event_description" id="event_description" type="text"></textarea>
    <br>
    <br>
    <label for="start_time">Start Time:</label>
    <br>
    <input name="start_time" id="start_time" type="datetime-local">
    <br>
    <br>
    <label for="end_time">End Time:</label>
    <br>
    <input name="end_time" id="end_time" type="datetime-local">
    <br>
    <br>
    <button type="submit">Submit</button>
  </form>
  <pre id="content" style="white-space: pre-wrap;"></pre>
  <script type="text/javascript">
    /* exported gapiLoaded */
    /* exported gisLoaded */
    /* exported handleAuthClick */
    /* exported handleSignoutClick */
    const CLIENT_ID = 'YOUR_ACTUAL_CLIENT_ID'; // 替换为你的Google Cloud客户端ID
    const API_KEY = null;
    const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest';
    const SCOPES = 'https://www.googleapis.com/auth/calendar';
    let tokenClient;
    let gapiInited = false;
    let gisInited = false;
    document.getElementById('authorize_button').style.visibility = 'hidden';
    document.getElementById('signout_button').style.visibility = 'hidden';
    document.getElementById('event_button').style.visibility = 'hidden';

    function gapiLoaded() {
      gapi.load('client', initializeGapiClient);
    }

    async function initializeGapiClient() {
      await gapi.client.init({
        apiKey: API_KEY,
        discoveryDocs: [DISCOVERY_DOC],
      });
      gapiInited = true;
      maybeEnableButtons();
    }

    function gisLoaded() {
      tokenClient = google.accounts.oauth2.initTokenClient({
        client_id: CLIENT_ID,
        scope: SCOPES,
        callback: '',
      });
      gisInited = true;
      maybeEnableButtons();
    }

    function maybeEnableButtons() {
      if (gapiInited && gisInited) {
        document.getElementById('authorize_button').style.visibility = 'visible';
        document.getElementById('event_button').style.visibility = 'visible';
      }
    }

    async function isFreeDuringEvent(startTime, endTime) {
      console.log(startTime);
      console.log(endTime);
      let response;
      try {
        const request = {
          'calendarId': 'primary',
          'timeMin': startTime,
          'timeMax': endTime,
          'singleEvents': true,
          'maxResults': 10,
          'orderBy': 'startTime',
          'timeZone': 'America/Los_Angeles' // 指定时区,确保时间解析正确
        };
        console.log(request);
        response = await gapi.client.calendar.events.list(request);
        console.log(response);
      } catch (err) {
        console.error('Error checking for conflicts:', err.message);
        return true; // 发生错误时默认视为存在冲突
      }

      // 检查response及result是否存在
      if (!response || !response.result) {
        console.error('Invalid response from Calendar API');
        return true;
      }

      const events = response.result.items || []; // 默认空数组避免undefined错误
      let hasEventDuringTimeRange = false;
      const targetStart = new Date(startTime);
      const targetEnd = new Date(endTime);

      for (const event of events) {
        // 处理全天事件和定时事件的时间字段
        const eventStart = event.start.dateTime ? new Date(event.start.dateTime) : new Date(event.start.date);
        const eventEnd = event.end.dateTime ? new Date(event.end.dateTime) : new Date(event.end.date);

        // 标准时间重叠检查逻辑:目标开始时间 < 事件结束时间 且 目标结束时间 > 事件开始时间
        if (targetStart < eventEnd && targetEnd > eventStart) {
          hasEventDuringTimeRange = true;
          console.log(`Conflict found: ${event.summary}`);
        }
      }

      if (!hasEventDuringTimeRange) {
        console.log('No conflicts found in the specified time range.');
      }

      return hasEventDuringTimeRange;
    }

    function handleAuthClick() {
      tokenClient.callback = async (resp) => {
        if (resp.error !== undefined) {
          throw (resp);
        }
        document.getElementById('signout_button').style.visibility = 'visible';
        document.getElementById('authorize_button').hidden = true;
        await listUpcomingEvents();
      };
      if (gapi.client.getToken() === null) {
        tokenClient.requestAccessToken({ prompt: 'consent' });
      } else {
        tokenClient.requestAccessToken({ prompt: '' });
      }
    }

    const formElement = document.querySelector('form#event_form')
    const getFormJSON = (form) => {
      const data = new FormData(form);
      return Array.from(data.keys()).reduce((result, key) => {
        result[key] = data.get(key);
        return result;
      }, {});
    };

    let submittedEventObj;
    const handler = (event) => {
      event.preventDefault();
      const valid = formElement.reportValidity();
      if (valid) {
        const result = getFormJSON(formElement);
        result.start_time = result.start_time + ':00';
        result.end_time = result.end_time + ':00';
        console.log(result);
        
        submittedEventObj = {
          'summary': result.event_summary,
          'location': result.event_location,
          'description': result.event_description,
          'start': {
            'dateTime': result.start_time,
            'timeZone': 'America/Los_Angeles',
          },
          'end': {
            'dateTime': result.end_time,
            'timeZone': 'America/Los_Angeles',
          }
        };
        console.log(submittedEventObj);

        // 统一处理认证后的逻辑
        async function handleAfterAuth() {
          const hasConflict = await isFreeDuringEvent(result.start_time, result.end_time);
          if (!hasConflict) {
            await createEvent(submittedEventObj);
          } else {
            console.log('Event creation canceled due to conflicts.');
          }
        }

        // 检查是否已认证,未认证则先请求令牌
        if (gapi.client.getToken() !== null) {
          tokenClient.callback = async (resp) => {
            if (resp.error !== undefined) {
              throw (resp);
            }
            await handleAfterAuth();
          };
          tokenClient.requestAccessToken({ prompt: '' });
        } else {
          tokenClient.callback = async (resp) => {
            if (resp.error !== undefined) {
              throw (resp);
            }
            document.getElementById('signout_button').style.visibility = 'visible';
            document.getElementById('authorize_button').hidden = true;
            await handleAfterAuth();
          };
          tokenClient.requestAccessToken({ prompt: 'consent' });
        }
      }
    };

    formElement.addEventListener("submit", handler);

    function handleEventClick() {
      tokenClient.callback = async (resp) => {
        const event = {
          'summary': 'Google I/O 2015',
          'location': '800 Howard St., San Francisco, CA 94103',
          'description': 'A chance to hear more about Google\'s developer products.',
          'start': {
            'dateTime': '2023-06-30T09:00:00',
            'timeZone': 'America/Los_Angeles',
          },
          'end': {
            'dateTime': '2023-06-30T17:00:00',
            'timeZone': 'America/Los_Angeles',
          }
        };
        if (resp.error !== undefined) {
          throw (resp);
        }
        await createEvent(event);
      };
      if (gapi.client.getToken() === null) {
        tokenClient.requestAccessToken({ prompt: 'consent' });
      } else {
        tokenClient.requestAccessToken({ prompt: '' });
      }
    }

    async function createEvent(submittedEvent) {
      try {
        const response = await gapi.client.calendar.events.insert({
          calendarId: 'primary',
          resource: submittedEvent,
        });
        console.log('Event created successfully:', response.result.htmlLink);
      } catch (err) {
        console.error('Error creating event:', err.message);
      }
    }

    function handleSignoutClick() {
      const token = gapi.client.getToken();
      if (token !== null) {
        google.accounts.oauth2.revoke(token.access_token);
        gapi.client.setToken('');
        document.getElementById('content').innerText = '';
        document.getElementById('authorize_button').innerText = 'Authorize';
        document.getElementById('signout_button').style.visibility = 'hidden';
      }
    }

    async function listUpcomingEvents() {
      let response;
      try {
        const request = {
          'calendarId': 'primary',
          'timeMin': (new Date()).toISOString(),
          'showDeleted': false,
          'singleEvents': true,
          'maxResults': 10,
          'orderBy': 'startTime',
        };
        response = await gapi.client.calendar.events.list(request);
      } catch (err) {
        document.getElementById('content').innerText = err.message;
        return;
      }
      const events = response.result.items;
      if (!events || events.length == 0) {
        document.getElementById('content').innerText = 'No events found.';
        return;
      }
      const output = events.reduce(
        (str, event) => `${str}${event.summary} (${event.start.dateTime || event.start.date})\n`,
        'Events:\n');
      document.getElementById('content').innerText = output;
    }
  </script>
  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>

内容的提问来源于stack exchange,提问作者displayName is undefined

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:18:09