From 4208996a8345d92256c7d13d6675a3b82f4a833a Mon Sep 17 00:00:00 2001 From: geekgeekrun Date: Sun, 13 Apr 2025 18:55:19 +0800 Subject: [PATCH] add gtag for renderer --- packages/ui/src/main/utils/initPublicIpc.ts | 32 +++++++++++++ packages/ui/src/renderer/src/App.vue | 7 +++ .../src/page/BootstrapSplash/index.vue | 4 ++ .../page/DownloadingDependencies.vue | 6 +++ .../src/page/CookieAssistant/index.vue | 34 +++++++++++--- .../src/page/FirstRunReadme/index.vue | 5 ++ .../src/renderer/src/page/LlmConfig/index.vue | 47 +++++++++++++++++-- .../src/page/MainLayout/BossLibrary.vue | 26 +++++++++- .../src/page/MainLayout/CompanyLibrary.vue | 24 +++++++++- .../MainLayout/GeekAutoStartChatWithBoss.vue | 20 ++++++-- .../src/page/MainLayout/JobLibrary.vue | 40 ++++++++++++++-- .../page/MainLayout/MarkAsNotSuitRecord.vue | 33 ++++++++++++- .../page/MainLayout/ReadNoReplyReminder.vue | 30 +++++++++++- .../src/page/MainLayout/StartChatRecord.vue | 31 +++++++++++- .../renderer/src/page/MainLayout/index.vue | 22 ++------- .../renderer/src/page/ResumeEditor/index.vue | 17 +++++++ packages/ui/src/renderer/src/router/index.ts | 9 +++- packages/ui/src/renderer/src/utils/gtag.ts | 17 +++++++ 18 files changed, 361 insertions(+), 43 deletions(-) create mode 100644 packages/ui/src/renderer/src/utils/gtag.ts diff --git a/packages/ui/src/main/utils/initPublicIpc.ts b/packages/ui/src/main/utils/initPublicIpc.ts index 4d8ea11..0b7e254 100644 --- a/packages/ui/src/main/utils/initPublicIpc.ts +++ b/packages/ui/src/main/utils/initPublicIpc.ts @@ -1,4 +1,7 @@ import { BrowserWindow, ipcMain, shell } from 'electron' +import gtag from './gtag' +import buildInfo from '../../common/build-info.json' +import os from 'node:os' export default function initPublicIpc() { ipcMain.on( @@ -23,4 +26,33 @@ export default function initPublicIpc() { activate: true }) }) + ipcMain.on('gtag', (ev, { name, params } = {}) => { + gtag(name, { + ...params, + electronLogSource: 'renderer' + }) + }) + ipcMain.on('send-feed-back-to-github-issue', (ev, payload) => { + const getIssueUrlWithBody = (issueBody: string = '') => { + const baseUrl = `https://github.com/geekgeekrun/geekgeekrun/issues/new` + issueBody = issueBody || '' + if (!issueBody || !issueBody.trim()) { + return baseUrl + } + const urlObj = new URL(baseUrl) + urlObj.searchParams.append('body', issueBody) + + return urlObj.toString() + } + + shell.openExternal( + getIssueUrlWithBody(`\n\n\n----- +版本号:${buildInfo.version}(${buildInfo.buildVersion}) +提交:${buildInfo.buildHash.substring(0, 6)} +操作系统信息: \`${os.type()}\` / \`${os.release()}\` / \`${os.arch()}\``), + { + activate: true + } + ) + }) } diff --git a/packages/ui/src/renderer/src/App.vue b/packages/ui/src/renderer/src/App.vue index cb291fa..e804103 100644 --- a/packages/ui/src/renderer/src/App.vue +++ b/packages/ui/src/renderer/src/App.vue @@ -9,4 +9,11 @@ diff --git a/packages/ui/src/renderer/src/page/BootstrapSplash/index.vue b/packages/ui/src/renderer/src/page/BootstrapSplash/index.vue index c42e141..d100754 100644 --- a/packages/ui/src/renderer/src/page/BootstrapSplash/index.vue +++ b/packages/ui/src/renderer/src/page/BootstrapSplash/index.vue @@ -26,6 +26,7 @@ import { useRouter } from 'vue-router' import { onMounted, ref } from 'vue' import { sleep } from '@geekgeekrun/utils/sleep.mjs' +import { gtagRenderer } from '@renderer/utils/gtag' const router = useRouter() @@ -33,10 +34,12 @@ const checkDependenciesResult = ref({}) const downloadProcessWaitee = ref(null) onMounted(async () => { + gtagRenderer('bootstrap_mounted') checkDependenciesResult.value = await electron.ipcRenderer.invoke('check-dependencies') downloadProcessWaitee.value = Promise.withResolvers() if (Object.values(checkDependenciesResult.value).includes(false)) { + gtagRenderer('dependencies_need_download') router.replace('/downloadingDependencies') } else { downloadProcessWaitee.value!.resolve() @@ -45,6 +48,7 @@ onMounted(async () => { downloadProcessWaitee.value!.promise.then(async () => { const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file') if (!isCookieFileValid) { + gtagRenderer('found_cookie_invalid_when_bootstrap') router.replace('/cookieAssistant') } else { await sleep(1000) diff --git a/packages/ui/src/renderer/src/page/BootstrapSplash/page/DownloadingDependencies.vue b/packages/ui/src/renderer/src/page/BootstrapSplash/page/DownloadingDependencies.vue index fbc663d..def3c45 100644 --- a/packages/ui/src/renderer/src/page/BootstrapSplash/page/DownloadingDependencies.vue +++ b/packages/ui/src/renderer/src/page/BootstrapSplash/page/DownloadingDependencies.vue @@ -13,6 +13,7 @@ diff --git a/packages/ui/src/renderer/src/page/MainLayout/JobLibrary.vue b/packages/ui/src/renderer/src/page/MainLayout/JobLibrary.vue index 67f74fb..dabff25 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/JobLibrary.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/JobLibrary.vue @@ -55,7 +55,17 @@
- 刷新 + 刷新
- 刷新
@@ -93,7 +101,12 @@
@@ -107,6 +120,7 @@ import { PageReq, PagedRes } from '../../../../common/types/pagination' import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue' import { MarkAsNotSuitReason } from '@geekgeekrun/sqlite-plugin/src/enums' import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs' +import { gtagRenderer } from '@renderer/utils/gtag' const tableData = ref([]) const pageSizeList = ref([100, 200, 300, 400]) @@ -122,6 +136,10 @@ const tableRef = ref>() const isTableLoading = ref(false) async function getMarkAsNotSuitRecord() { try { + gtagRenderer('mark_as_not_suit_record_request_sent', { + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) isTableLoading.value = true const { data: res } = (await electron.ipcRenderer.invoke('get-mark-as-not-suit-record', { pageNo: pagination.value.pageNo, @@ -133,7 +151,16 @@ async function getMarkAsNotSuitRecord() { pageNo: res.pageNo, pageSize: pagination.value.pageSize } + gtagRenderer('mark_as_not_suit_record_request_success', { + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) } catch (err) { + gtagRenderer('mark_as_not_suit_record_request_error', { + err, + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) console.log(err) tableData.value = [] } finally { @@ -158,6 +185,7 @@ onMounted(() => { }) async function handleViewJobOnlineButtonClick(encryptJobId: string) { + gtagRenderer('view_job_online_button_clicked') return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', { url: `https://www.zhipin.com/job_detail/${encryptJobId}.html` }) @@ -167,6 +195,7 @@ const drawVisibleModelValue = ref(false) const selectedJobInfoForViewSnapshot = ref(null) function handleViewJobSnapshotButtonClick(record: VMarkAsNotSuitLog) { + gtagRenderer('view_job_snapshot_button_clicked') selectedJobInfoForViewSnapshot.value = record drawVisibleModelValue.value = true } diff --git a/packages/ui/src/renderer/src/page/MainLayout/ReadNoReplyReminder.vue b/packages/ui/src/renderer/src/page/MainLayout/ReadNoReplyReminder.vue index a17583a..2d209a9 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/ReadNoReplyReminder.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/ReadNoReplyReminder.vue @@ -73,7 +73,16 @@ 使用外部编辑器编辑提示词模板 - + 还原默认提示词模板 @@ -139,6 +148,8 @@ import { computed, nextTick, onUnmounted, ref, watch } from 'vue' import { dayjs, ElForm, ElMessage, ElMessageBox } from 'element-plus' import { useRouter } from 'vue-router' import { RECHAT_CONTENT_SOURCE } from '../../../../common/enums/auto-start-chat' +import { gtagRenderer } from '@renderer/utils/gtag' + const router = useRouter() const formContent = ref({ autoReminder: { @@ -155,8 +166,10 @@ const enableRechatLimit = computed({ }, set(val) { if (!val) { + gtagRenderer('rechat_limit_disabled') formContent.value.autoReminder.rechatLimitDay = 0 } else { + gtagRenderer('rechat_limit_enabled') formContent.value.autoReminder.rechatLimitDay = 21 } } @@ -213,12 +226,17 @@ watch( ) const handleSubmit = async () => { + gtagRenderer('save_config_clicked', { + ...formContent.value?.autoReminder + }) await formRef.value!.validate() await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) + gtagRenderer('config_saved') try { await electron.ipcRenderer.invoke('check-if-auto-remind-prompt-valid') } catch (err) { if (err?.message?.includes(`RESUME_PLACEHOLDER_NOT_EXIST`)) { + gtagRenderer('cannot_launch_due_to_resume_placeholder_not_exist') console.log(`提示词模板无效`, err) ElMessageBox.confirm( '提示词模板缺少简历内容占位符:
__REPLACE_REAL_RESUME_HERE__

您是否希望还原默认的提示词模板?', @@ -232,10 +250,14 @@ const handleSubmit = async () => { } ) .then(async () => { + gtagRenderer('reset_template_clicked_in_invalid_tip_dialog') await restoreDefaultTemplate() }) - .catch(() => {}) + .catch(() => { + gtagRenderer('close_invalid_tip_dialog') + }) } else { + gtagRenderer('cannot_launch_due_to_unknown_error', { err }) ElMessage({ type: 'error', message: '用于生成自动提醒消息的提示词检查未通过,请重试' @@ -243,6 +265,7 @@ const handleSubmit = async () => { } return } + gtagRenderer('reminder_launched') router.replace({ path: '/geekAutoStartChatWithBoss/prepareRun', query: { flow: 'read-no-reply-reminder' } @@ -287,6 +310,7 @@ const rechatLimitDateString = computed(() => { }) const handleClickConfigLlm = async () => { + gtagRenderer('config_llm_clicked') try { await electron.ipcRenderer.invoke('llm-config') } catch (err) { @@ -295,6 +319,7 @@ const handleClickConfigLlm = async () => { } const handleClickEditResume = async () => { + gtagRenderer('edit_resume_clicked') try { await electron.ipcRenderer.invoke('resume-edit') } catch (err) { @@ -303,6 +328,7 @@ const handleClickEditResume = async () => { } const handleClickEditPrompt = async () => { + gtagRenderer('edit_prompt_clicked') await electron.ipcRenderer.send('no-reply-reminder-prompt-edit') } diff --git a/packages/ui/src/renderer/src/page/MainLayout/StartChatRecord.vue b/packages/ui/src/renderer/src/page/MainLayout/StartChatRecord.vue index 978d41d..d049ba7 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/StartChatRecord.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/StartChatRecord.vue @@ -55,7 +55,15 @@
- 刷新
@@ -76,7 +84,12 @@
@@ -89,6 +102,7 @@ import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VCha import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs' import { PageReq, PagedRes } from '../../../../common/types/pagination' import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue' +import { gtagRenderer } from '@renderer/utils/gtag' const tableData = ref([]) const pageSizeList = ref([100, 200, 300, 400]) @@ -104,6 +118,10 @@ const tableRef = ref>() const isTableLoading = ref(false) async function getAutoStartChatRecord() { try { + gtagRenderer('start_chat_record_request_sent', { + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) isTableLoading.value = true const { data: res } = (await electron.ipcRenderer.invoke('get-auto-start-chat-record', { pageNo: pagination.value.pageNo, @@ -115,7 +133,16 @@ async function getAutoStartChatRecord() { pageNo: res.pageNo, pageSize: pagination.value.pageSize } + gtagRenderer('start_chat_record_request_success', { + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) } catch (err) { + gtagRenderer('start_chat_record_request_error', { + err, + page_no: pagination.value.pageNo, + page_size: pagination.value.pageSize, + }) console.log(err) tableData.value = [] } finally { diff --git a/packages/ui/src/renderer/src/page/MainLayout/index.vue b/packages/ui/src/renderer/src/page/MainLayout/index.vue index d1a257b..6ab38e5 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/index.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/index.vue @@ -41,6 +41,7 @@ import { ElIcon } from 'element-plus' import { TopRight } from '@element-plus/icons-vue' import useBuildInfo from '@renderer/hooks/useBuildInfo' import { debounce } from 'lodash-es' +import { gtagRenderer } from '@renderer/utils/gtag' const router = useRouter() const unmountedCbs: Array> = [] onUnmounted(() => { @@ -76,31 +77,18 @@ onUnmounted(() => { })() const { buildInfo } = useBuildInfo() -const getIssueUrlWithBody = (issueBody: string = '') => { - const baseUrl = `https://github.com/geekgeekrun/geekgeekrun/issues/new` - issueBody = issueBody || '' - if (!issueBody || !issueBody.trim()) { - return baseUrl - } - const urlObj = new URL(baseUrl) - urlObj.searchParams.append('body', issueBody) - - return urlObj.toString() -} const handleFeedbackClick = () => { - electron.ipcRenderer.send( - 'open-external-link', - getIssueUrlWithBody(`\n\n\n----- -版本号:${buildInfo.value.version}(${buildInfo.value.buildVersion}) -提交:${buildInfo.value.buildHash.substring(0, 6)}`) - ) + gtagRenderer('goto_feedback_clicked') + electron.ipcRenderer.send('send-feed-back-to-github-issue') } const handleGotoProjectPageClick = () => { + gtagRenderer('goto_project_github_clicked') electron.ipcRenderer.send('open-external-link', 'https://github.com/geekgeekrun/geekgeekrun') } const handleLaunchBossSite = debounce( async () => { + gtagRenderer('launch-boss-site-clicked') return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', { url: `https://www.zhipin.com/` }) diff --git a/packages/ui/src/renderer/src/page/ResumeEditor/index.vue b/packages/ui/src/renderer/src/page/ResumeEditor/index.vue index 5a26d13..ad41ad3 100644 --- a/packages/ui/src/renderer/src/page/ResumeEditor/index.vue +++ b/packages/ui/src/renderer/src/page/ResumeEditor/index.vue @@ -313,6 +313,7 @@ import { ElForm, ElButton, ElAlert } from 'element-plus' import { ref, onMounted } from 'vue' import { ArrowUp, ArrowDown, Delete, Plus } from '@element-plus/icons-vue' +import { gtagRenderer } from '@renderer/utils/gtag' interface ResumeContent { name: string @@ -360,10 +361,14 @@ const formContent = ref(getEmptyFormContent()) const formRules = {} const handleCancel = () => { + gtagRenderer('cancel_clicked') electron.ipcRenderer.send('close-resume-editor') + gtagRenderer('cancel_done') } const handleSubmit = async () => { + gtagRenderer('submit_clicked') electron.ipcRenderer.invoke('save-resume-content', JSON.parse(JSON.stringify(formContent.value))) + gtagRenderer('submit_done') } onMounted(async () => { @@ -410,12 +415,14 @@ function getNewWorkExpItem() { } function addWorkExp() { formContent.value.geekWorkExpList.push(getNewWorkExpItem()) + gtagRenderer('resume_work_exp_added') } function moveWorkExpUp(index) { ;[formContent.value.geekWorkExpList[index], formContent.value.geekWorkExpList[index - 1]] = [ formContent.value.geekWorkExpList[index - 1], formContent.value.geekWorkExpList[index] ] + gtagRenderer('resume_work_exp_moved_up') } function moveWorkExpDown(index) { @@ -423,10 +430,12 @@ function moveWorkExpDown(index) { formContent.value.geekWorkExpList[index + 1], formContent.value.geekWorkExpList[index] ] + gtagRenderer('resume_work_exp_moved_down') } function removeWorkExp(index) { formContent.value.geekWorkExpList.splice(index, 1) + gtagRenderer('resume_work_exp_removed') } // #endregion @@ -443,12 +452,14 @@ function getNewProjExpItem() { } function addProjExp() { formContent.value.geekProjExpList.push(getNewProjExpItem()) + gtagRenderer('resume_proj_exp_added') } function moveProjExpUp(index) { ;[formContent.value.geekProjExpList[index], formContent.value.geekProjExpList[index - 1]] = [ formContent.value.geekProjExpList[index - 1], formContent.value.geekProjExpList[index] ] + gtagRenderer('resume_proj_exp_moved_up') } function moveProjExpDown(index) { @@ -456,12 +467,18 @@ function moveProjExpDown(index) { formContent.value.geekProjExpList[index + 1], formContent.value.geekProjExpList[index] ] + gtagRenderer('resume_proj_exp_moved_down') } function removeProjExp(index) { formContent.value.geekProjExpList.splice(index, 1) + gtagRenderer('resume_proj_exp_removed') } // #endregion + +onMounted(() => { + gtagRenderer('resume_editor_mounted') +})