diff --git a/Touchkebao/devlop.py b/Touchkebao/devlop.py index 48ccd126..4ffbbf2f 100644 --- a/Touchkebao/devlop.py +++ b/Touchkebao/devlop.py @@ -55,13 +55,52 @@ success('本地打包完成') # 3. 上传 step('Step 3: 上传 zip 包到服务器') info('开始上传 zip 包...') + +# 获取本地文件大小 +local_size = os.path.getsize(zip_name) +info(f'本地文件大小: {local_size / 1024 / 1024:.2f} MB') + transport = paramiko.Transport((server_ip, server_port)) transport.connect(username=server_user, password=server_pwd) sftp = paramiko.SFTPClient.from_transport(transport) -sftp.put(zip_name, remote_path) -sftp.close() -transport.close() -success('上传到服务器完成') + +try: + # 如果远程文件已存在,先删除 + try: + sftp.remove(remote_path) + info('已删除远程旧文件') + except: + pass # 文件不存在,忽略 + + # 确保远程目录存在 + remote_dir = os.path.dirname(remote_path) + try: + sftp.stat(remote_dir) + except: + error(f'远程目录不存在: {remote_dir}') + raise + + # 上传进度回调 + def progress_callback(transferred, total): + percent = (transferred / total) * 100 + if transferred % (1024 * 1024) == 0 or transferred == total: # 每MB或完成时显示 + info(f'上传进度: {transferred / 1024 / 1024:.2f}MB / {total / 1024 / 1024:.2f}MB ({percent:.1f}%)') + + # 上传文件 + sftp.put(zip_name, remote_path, callback=progress_callback, confirm=True) + + # 验证远程文件大小 + remote_size = sftp.stat(remote_path).st_size + info(f'远程文件大小: {remote_size / 1024 / 1024:.2f} MB') + + if remote_size != local_size: + raise IOError(f'文件大小不匹配!本地: {local_size}, 远程: {remote_size}') + + success('上传到服务器完成') + +finally: + sftp.close() + transport.close() # 删除本地 dist.zip try: diff --git a/Touchkebao/src/pages/pc/ckbox/data.ts b/Touchkebao/src/pages/pc/ckbox/data.ts index 6e006858..037a4367 100644 --- a/Touchkebao/src/pages/pc/ckbox/data.ts +++ b/Touchkebao/src/pages/pc/ckbox/data.ts @@ -114,6 +114,7 @@ export interface weChatGroup { nickname: string; chatroomAvatar: string; groupId: number; + aiType?: number; // AI类型(0=普通,1=AI辅助) config?: { top?: false; chat?: boolean; @@ -151,6 +152,7 @@ export interface ContractData { isPassed: boolean; tenantId: number; groupId: number; + aiType?: number; // AI类型(0=普通,1=AI辅助) thirdParty: null; additionalPicture: string; desc: string; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx index d571fc19..7e7b4d41 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx @@ -1,5 +1,13 @@ import React, { useState, useEffect } from "react"; -import { Layout, Button, Avatar, Space, Tooltip, Dropdown } from "antd"; +import { + Layout, + Button, + Avatar, + Space, + Tooltip, + Dropdown, + message, +} from "antd"; import { UserOutlined, TeamOutlined, @@ -19,6 +27,9 @@ import TodoListModal from "./components/TodoListModal"; import ChatRecordSearch from "./components/ChatRecordSearch"; import { setFriendInjectConfig } from "@/pages/pc/ckbox/weChat/api"; import { useWeChatStore } from "@/store/module/weChat/weChat"; +import { MessageManager } from "@/utils/dbAction/message"; +import { ContactManager } from "@/utils/dbAction/contact"; +import { useUserStore } from "@storeModule/user"; const { Header, Content } = Layout; interface ChatWindowProps { @@ -41,6 +52,10 @@ const ChatWindow: React.FC = ({ contract }) => { const showChatRecordModel = useWeChatStore( state => state.showChatRecordModel, ); + const setCurrentContact = useWeChatStore(state => state.setCurrentContact); + const { user } = useUserStore(); + const currentUserId = user?.id || 0; + const [showProfile, setShowProfile] = useState(true); const [followupModalVisible, setFollowupModalVisible] = useState(false); const [todoModalVisible, setTodoModalVisible] = useState(false); @@ -76,20 +91,72 @@ const ChatWindow: React.FC = ({ contract }) => { }, [aiQuoteMessageContent]); // 处理配置选择 - const handleConfigChange = option => { + const handleConfigChange = async option => { setCurrentConfig({ value: option.value, label: option.label, }); - // 保存配置到后端 - setFriendInjectConfig({ - type: option.value, - wechatAccountId: contract.wechatAccountId, - friendId: contract.id, - }).then(() => { + try { + // 1. 保存配置到后端 + await setFriendInjectConfig({ + type: option.value, + wechatAccountId: contract.wechatAccountId, + friendId: contract.id, + }); + + // 2. 更新 Store 中的 AI 配置状态 updateAiQuoteMessageContent(option.value); - }); + + // 3. 确定联系人类型 + const contactType: "friend" | "group" = contract.chatroomId + ? "group" + : "friend"; + const aiType = option.value; + + console.log( + `开始更新 aiType: contactId=${contract.id}, type=${contactType}, aiType=${aiType}`, + ); + + // 4. 更新会话列表数据库的 aiType + await MessageManager.updateSession({ + userId: currentUserId, + id: contract.id!, + type: contactType, + aiType: aiType, + }); + console.log("✅ 会话列表数据库 aiType 更新成功"); + + // 5. 更新联系人数据库的 aiType + const contactInDb = await ContactManager.getContactByIdAndType( + currentUserId, + contract.id!, + contactType, + ); + + if (contactInDb) { + await ContactManager.updateContact({ + ...contactInDb, + aiType: aiType, + }); + console.log("✅ 联系人数据库 aiType 更新成功"); + } else { + console.warn("⚠️ 联系人数据库中未找到该联系人"); + } + + // 6. 更新 Store 中的 currentContract(通过重新设置) + const updatedContract = { + ...contract, + aiType: aiType, + }; + setCurrentContact(updatedContract); + console.log("✅ Store currentContract aiType 更新成功"); + + message.success(`已切换为${option.label}`); + } catch (error) { + console.error("更新 AI 配置失败:", error); + message.error("配置更新失败,请重试"); + } }; return ( diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx index 2cf63cdc..b24c8343 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx @@ -709,6 +709,9 @@ const MessageList: React.FC = () => { // 点击会话 const onContactClick = async (session: ChatSession) => { + console.log("onContactClick", session); + console.log("session.aiType:", session.aiType); // 调试:查看 aiType 字段 + // 设置当前会话 setCurrentContact(session as any); diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/data.ts b/Touchkebao/src/pages/pc/ckbox/weChat/data.ts index 52605792..7b0a7aca 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/data.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/data.ts @@ -112,6 +112,7 @@ export interface weChatGroup { nickname: string; chatroomAvatar: string; groupId: number; + aiType?: number; // AI类型(0=普通,1=AI辅助) config?: { top?: false; chat?: boolean; @@ -149,6 +150,7 @@ export interface ContractData { isPassed: boolean; tenantId: number; groupId: number; + aiType?: number; // AI类型(0=普通,1=AI辅助) thirdParty: null; additionalPicture: string; desc: string; diff --git a/Touchkebao/src/store/module/weChat/weChat.ts b/Touchkebao/src/store/module/weChat/weChat.ts index abeb9c19..935e0163 100644 --- a/Touchkebao/src/store/module/weChat/weChat.ts +++ b/Touchkebao/src/store/module/weChat/weChat.ts @@ -138,6 +138,10 @@ export const useWeChatStore = create()( }, /** 设置当前联系人并加载相关数据 */ setCurrentContact: (contract: ContractData | weChatGroup) => { + console.log( + "setCurrentContact - contract.aiType:", + (contract as any).aiType, + ); // 调试:查看 aiType 字段 const state = useWeChatStore.getState(); // 切换联系人时清空当前消息,等待重新加载 set({ currentMessages: [] }); @@ -294,7 +298,10 @@ export const useWeChatStore = create()( })); // 只有文字消息才触发AI(msgType === 1) - if (message.msgType === 1) { + if ( + message.msgType === 1 && + (currentContract as any).aiType === 1 + ) { //把数据传到存客宝 const params: any = { type: "CmdNewMessage", diff --git a/Touchkebao/src/utils/db.ts b/Touchkebao/src/utils/db.ts index d314eca9..abd22d02 100644 --- a/Touchkebao/src/utils/db.ts +++ b/Touchkebao/src/utils/db.ts @@ -41,6 +41,7 @@ export interface ChatSession { avatar: string; // 头像 content: string; // 最新消息内容 lastUpdateTime: string; // 最后更新时间 + aiType?: number; // AI类型(0=普通,1=AI辅助) config: { unreadCount: number; // 未读数量 top: number; // 是否置顶(1=置顶,0=非置顶) @@ -72,6 +73,7 @@ export interface Contact { conRemark?: string; // 备注名 avatar: string; // 头像 lastUpdateTime: string; // 最后更新时间 + aiType?: number; // AI类型(0=普通,1=AI辅助) config?: any; // 配置信息 sortKey: string; // 预计算排序键 searchKey: string; // 预计算搜索键 @@ -142,6 +144,47 @@ class CunkebaoDatabase extends Dexie { userLoginRecords: "serverId, userId, lastLoginTime, loginCount, createTime, lastActiveTime", }); + + // 版本2:添加 aiType 字段 + this.version(2) + .stores({ + // 会话表索引:添加 aiType 索引 + chatSessions: + "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], [userId+aiType], sortKey, nickname, conRemark, avatar, content, lastUpdateTime, aiType", + + // 联系人表索引:添加 aiType 索引 + contactsUnified: + "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+aiType], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime, groupId, aiType", + + // 联系人标签映射表索引:保持不变 + contactLabelMap: + "serverId, userId, labelId, contactId, contactType, [userId+labelId], [userId+contactId], [userId+labelId+sortKey], sortKey, searchKey, avatar, nickname, conRemark, unreadCount, lastUpdateTime", + + // 用户登录记录表索引:保持不变 + userLoginRecords: + "serverId, userId, lastLoginTime, loginCount, createTime, lastActiveTime", + }) + .upgrade(tx => { + // 数据迁移:为现有数据添加 aiType 默认值 + return tx + .table("chatSessions") + .toCollection() + .modify(session => { + if (session.aiType === undefined) { + session.aiType = 0; // 默认为普通类型 + } + }) + .then(() => { + return tx + .table("contactsUnified") + .toCollection() + .modify(contact => { + if (contact.aiType === undefined) { + contact.aiType = 0; // 默认为普通类型 + } + }); + }); + }); } } diff --git a/Touchkebao/src/utils/dbAction/contact.ts b/Touchkebao/src/utils/dbAction/contact.ts index 675e0650..254f2ec8 100644 --- a/Touchkebao/src/utils/dbAction/contact.ts +++ b/Touchkebao/src/utils/dbAction/contact.ts @@ -183,7 +183,8 @@ export class ContactManager { local.nickname !== server.nickname || local.conRemark !== server.conRemark || local.avatar !== server.avatar || - local.wechatAccountId !== server.wechatAccountId + local.wechatAccountId !== server.wechatAccountId || + (local.aiType ?? 0) !== (server.aiType ?? 0) // 添加 aiType 比较 ); } diff --git a/Touchkebao/src/utils/dbAction/message.ts b/Touchkebao/src/utils/dbAction/message.ts index ae0a0e20..e1dfa5ea 100644 --- a/Touchkebao/src/utils/dbAction/message.ts +++ b/Touchkebao/src/utils/dbAction/message.ts @@ -91,6 +91,7 @@ export class MessageManager { avatar: friend.avatar || "", content: (friend as any).content || "", lastUpdateTime: friend.lastUpdateTime || new Date().toISOString(), + aiType: (friend as any).aiType ?? 0, // AI类型,默认为0(普通) config: { unreadCount: friend.config?.unreadCount || 0, top: (friend.config as any)?.top || false, @@ -123,6 +124,7 @@ export class MessageManager { avatar: group.chatroomAvatar || "", content: (group as any).content || "", lastUpdateTime: (group as any).lastUpdateTime || new Date().toISOString(), + aiType: (group as any).aiType ?? 0, // AI类型,默认为0(普通) config: { unreadCount: (group.config as any)?.unreadCount || 0, top: (group.config as any)?.top || false, @@ -195,6 +197,7 @@ export class MessageManager { "conRemark", "avatar", "wechatAccountId", // 添加wechatAccountId比较 + "aiType", // 添加aiType比较 ]; for (const field of fieldsToCompare) {