const { checkAuth, createAgentRun, getAgentRun, getMe, loadSession, logout } = require('../../utils/api'); const { AGENT_RUN_MAX_POLLS, AGENT_RUN_POLL_MS, SELECTED_SESSION_KEY, API_BASE_URL } = require('../../utils/config'); const { createUserMessage, normalizeMessages } = require('../../utils/messages'); const { buildMessageView, resolveWorkspacePublicUrl } = require('../../utils/message-display'); function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } Page({ data: { sessionId: '', messages: [], draft: '', loading: false, error: '', scrollIntoView: '' }, async onShow() { if (this._authCheckPromise) { await this._authCheckPromise; return; } this._authCheckPromise = this.verifyAccess(); try { await this._authCheckPromise; } finally { this._authCheckPromise = null; } }, async verifyAccess() { if (this._redirectingToLogin) return; try { const status = await checkAuth(); if (!status?.authenticated) { this._redirectingToLogin = true; wx.redirectTo({ url: '/pages/login/index', complete: () => { this._redirectingToLogin = false; }, }); return; } await this.ensureCurrentUser(); const selectedSessionId = wx.getStorageSync(SELECTED_SESSION_KEY); if (selectedSessionId) { wx.removeStorageSync(SELECTED_SESSION_KEY); if (selectedSessionId !== this.data.sessionId) { await this.openSession(selectedSessionId); } } else if (this.data.sessionId && this.shouldRefreshRenderedMessages()) { await this.refreshSession(this.data.sessionId); } } catch { if (this._redirectingToLogin) return; this._redirectingToLogin = true; wx.redirectTo({ url: '/pages/login/index', complete: () => { this._redirectingToLogin = false; }, }); } }, getMessageRenderOptions() { const app = getApp(); const user = app?.globalData?.user || {}; return { publishKey: user.id || user.publishSlug || '', apiBaseUrl: API_BASE_URL, }; }, shouldRefreshRenderedMessages() { return this.data.messages.some( (item) => !Array.isArray(item.parts) || (/\bpublic\/[A-Za-z0-9._-]+\.html\b/i.test(item.text || '') && (!item.pageLinks || item.pageLinks.length === 0)) ); }, async ensureCurrentUser() { const app = getApp(); if (app?.globalData?.user?.id) return app.globalData.user; const result = await getMe(); if (result?.user) { app.globalData.user = result.user; } return result?.user || null; }, handleInput(event) { this.setData({ draft: event.detail.value }); }, scrollToBottom() { const index = Math.max(0, this.data.messages.length - 1); this.setData({ scrollIntoView: `msg-${index}` }); }, async sendMessage() { const text = this.data.draft.trim(); if (!text || this.data.loading) return; const userMessage = createUserMessage(text); const userView = buildMessageView(userMessage, this.getMessageRenderOptions()); const nextMessages = [...this.data.messages, userView]; this.setData({ messages: nextMessages, draft: '', loading: true, error: '' }); this.scrollToBottom(); try { const payload = { session_id: this.data.sessionId || undefined, user_message: userMessage }; console.info('[TKMind miniapp] createAgentRun start', { sessionId: payload.session_id || null }); const run = await createAgentRun(payload); console.info('[TKMind miniapp] agent run created', run?.id, run?.status); await this.waitForRun(run); } catch (error) { console.error('[TKMind miniapp] send failed', error); this.setData({ error: error?.message || '发送失败' }); wx.showToast({ title: error?.message || '发送失败', icon: 'none' }); } finally { this.setData({ loading: false }); this.scrollToBottom(); } }, async waitForRun(run) { const runId = run?.id; if (!runId) throw new Error('后台任务未返回 runId'); let sessionId = run?.sessionId || this.data.sessionId; for (let i = 0; i < AGENT_RUN_MAX_POLLS; i += 1) { const latest = i === 0 ? run : await getAgentRun(runId); if (latest?.sessionId) { sessionId = latest.sessionId; if (sessionId !== this.data.sessionId) { this.setData({ sessionId }); } } if (latest?.status === 'succeeded') { if (sessionId) { await this.refreshSession(sessionId); } else { throw new Error('任务已完成,但未返回会话 ID'); } return; } if (latest?.status === 'failed') { throw new Error(latest.error || '后台任务失败'); } await delay(AGENT_RUN_POLL_MS); } throw new Error('任务耗时较长,请稍后在会话中查看'); }, async refreshSession(sessionId) { const detail = await loadSession(sessionId); const conversation = detail?.conversation || detail?.messages || detail?.data?.conversation || detail?.data?.messages || []; const visible = conversation.filter( (message) => message?.metadata?.userVisible !== false ); const messages = normalizeMessages(visible, this.getMessageRenderOptions()); this.setData({ sessionId, messages, error: '' }); }, async openSession(sessionId) { this.setData({ loading: true, error: '' }); try { await this.refreshSession(sessionId); } catch (error) { this.setData({ error: error?.message || '会话加载失败' }); } finally { this.setData({ loading: false }); this.scrollToBottom(); } }, startNewChat() { wx.removeStorageSync(SELECTED_SESSION_KEY); this.setData({ sessionId: '', messages: [], draft: '', error: '' }); }, openLegalCenter() { wx.navigateTo({ url: '/pages/legal/index/index' }); }, openLink(event) { let url = String(event.currentTarget.dataset.url || '').trim(); if (!url) { wx.showToast({ title: '链接无效', icon: 'none' }); return; } const { publishKey, apiBaseUrl } = this.getMessageRenderOptions(); if (/^public\//i.test(url)) { const resolved = resolveWorkspacePublicUrl(url, publishKey, apiBaseUrl); if (resolved) url = resolved; } else if (url.startsWith('/')) { url = `${String(apiBaseUrl).replace(/\/$/, '')}${url}`; } wx.navigateTo({ url: `/pages/webview/index?url=${encodeURIComponent(url)}` }); }, copyMessage(event) { const text = String(event.currentTarget.dataset.text || '').trim(); if (!text) return; wx.setClipboardData({ data: text, success() { wx.showToast({ title: '已复制', icon: 'success' }); }, fail() { wx.showToast({ title: '复制失败', icon: 'none' }); } }); }, async handleLogout() { this.setData({ loading: true, error: '' }); try { await logout(); wx.redirectTo({ url: '/pages/login/index' }); } catch (error) { this.setData({ error: error?.message || '退出失败' }); } finally { this.setData({ loading: false }); } } });