feat: 个人微信-对话历史持久化+平台内查阅页; 修复第二轮不回复(过滤/字段/重试); 多轮上下文

This commit is contained in:
WorkBuddy committed 2026-07-10 07:01:39 +08:00
1 parent 2eadd83304
commit 62f3986e04
5 files changed
+251 -28

No files matched your search

+2
View File
@@ -23,6 +23,7 @@ import AssetsPage from '@/pages/front/AssetsPage';
import NovelPreviewPage from '@/pages/front/NovelPreviewPage';
import EvolutionDashboard from '@/pages/front/EvolutionDashboard';
import WechatBot from '@/pages/front/WechatBot';
import WechatHistory from '@/pages/front/WechatHistory';
// Admin Pages
import AdminDashboard from '@/pages/admin/Dashboard';
@@ -119,6 +120,7 @@ const App: React.FC = () => {
<Route path="assets/novel/:id" element={<NovelPreviewPage />} />
<Route path="evolution" element={<EvolutionDashboard />} />
<Route path="wechat-bot" element={<WechatBot />} />
<Route path="wechat-history" element={<WechatHistory />} />
</Route>
{/* Admin Routes */}
+1
View File
@@ -1000,4 +1000,5 @@ export const imUserAPI = {
qrcode: () => post<any>('/im/wechat/qrcode'),
qrcodeStatus: (qrcode: string) => post<any>(`/im/wechat/qrcode/status?qrcode=${encodeURIComponent(qrcode)}`),
disconnect: () => post<any>('/im/wechat/disconnect'),
history: () => get<any>('/im/wechat/history'),
};
@@ -18,6 +18,7 @@ const navGroups = [
items: [
{ path: '/channels', icon: 'channel', label: '频道管理' },
{ path: '/wechat-bot', icon: 'wechat', label: '微信机器人' },
{ path: '/wechat-history', icon: 'chat', label: '对话记录' },
],
},
{
@@ -0,0 +1,97 @@
// @ts-nocheck
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { imUserAPI } from '@/api';
const WechatHistory: React.FC = () => {
const [loading, setLoading] = useState(true);
const [messages, setMessages] = useState<any[]>([]);
const [toast, setToast] = useState<{ msg: string; type: 'success' | 'error' } | null>(null);
const bottomRef = useRef<HTMLDivElement>(null);
const showToast = (msg: string, type: 'success' | 'error' = 'success') => {
setToast({ msg, type });
setTimeout(() => setToast(null), 3000);
};
const load = useCallback(async () => {
setLoading(true);
try {
const res = await imUserAPI.history();
if ((res as any)?.success && (res as any).data) {
setMessages((res as any).data.messages || []);
} else {
showToast('加载失败', 'error');
}
} catch (e) {
showToast('加载失败', 'error');
} finally {
setLoading(false);
}
}, []);
useEffect(() => { load(); }, [load]);
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]);
const formatTime = (t: string) => (t ? new Date(t).toLocaleString('zh-CN') : '-');
return (
<div style={{ minHeight: '100%', background: '#FFFFFF', color: '#1A1F36', padding: '32px' }}>
<div style={{ maxWidth: '860px', margin: '0 auto' }}>
{/* Header */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
<div>
<h1 style={{ fontSize: '24px', fontWeight: 700, margin: 0, color: '#1A1F36' }}>微信对话记录</h1>
<p style={{ color: '#6B7280', fontSize: '14px', margin: '6px 0 0 0' }}>
你通过个人微信与七虎AI 的对话历史,按时间顺序记录,可随时回看(仅本人可见,按账号隔离)。
</p>
</div>
<button onClick={load} disabled={loading} style={btnStyle}>{loading ? '刷新中…' : '刷新'}</button>
</div>
{/* Toast */}
{toast && (
<div style={{
position: 'fixed', top: '20px', right: '20px', zIndex: 9999,
padding: '12px 24px', borderRadius: '8px',
background: toast.type === 'success' ? '#10b981' : '#ef4444',
color: '#fff', fontSize: '14px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
}}>{toast.msg}</div>
)}
{/* 记录列表 */}
<div style={{ background: '#F8F9FC', borderRadius: '12px', padding: '16px', minHeight: '420px' }}>
{messages.length === 0 ? (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '380px', color: '#9CA3AF', fontSize: '14px', textAlign: 'center', padding: '0 24px' }}>
{loading ? '加载中…' : '暂无对话记录。请先在「微信机器人」中绑定并发送消息,对话记录会显示在这里。'}
</div>
) : (
messages.map((m, i) => (
<div key={i} style={{ display: 'flex', justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start', marginBottom: '14px' }}>
<div style={{ maxWidth: '78%' }}>
<div style={{ fontSize: '11px', color: '#9CA3AF', marginBottom: '4px', textAlign: m.role === 'user' ? 'right' : 'left' }}>
{m.role === 'user' ? '我(微信)' : '七虎AI'} · {formatTime(m.created_at)}
</div>
<div style={{
padding: '10px 14px', borderRadius: '12px', fontSize: '14px', lineHeight: '1.6',
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
background: m.role === 'user' ? '#6C63FF' : '#FFFFFF',
color: m.role === 'user' ? '#fff' : '#1A1F36',
border: m.role === 'user' ? 'none' : '1px solid #E5E7EB',
}}>{m.content}</div>
</div>
</div>
))
)}
<div ref={bottomRef} />
</div>
</div>
</div>
);
};
const btnStyle: React.CSSProperties = {
padding: '8px 20px', background: '#6C63FF', border: 'none', borderRadius: '6px',
color: '#fff', fontSize: '14px', fontWeight: 500, cursor: 'pointer',
};
export default WechatHistory;