From 170192c7a3c7d353c6a137978ceb2816a99b1c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Thu, 3 Apr 2025 10:12:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=8F=B7=E7=9A=84=E5=A5=BD?= =?UTF-8?q?=E5=8F=8B=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/api/wechat-accounts.ts | 37 +- Cunkebao/app/wechat-accounts/[id]/page.tsx | 580 ++++++++++-------- Server/application/devices/config/route.php | 1 + .../devices/controller/DeviceWechat.php | 87 ++- .../devices/model/WechatFriend.php | 84 +++ 5 files changed, 527 insertions(+), 262 deletions(-) create mode 100644 Server/application/devices/model/WechatFriend.php diff --git a/Cunkebao/api/wechat-accounts.ts b/Cunkebao/api/wechat-accounts.ts index 3097b1eb..8190dd34 100755 --- a/Cunkebao/api/wechat-accounts.ts +++ b/Cunkebao/api/wechat-accounts.ts @@ -204,4 +204,39 @@ const mapRestrictionType = (type: string): "friend_limit" | "marketing" | "spam" }; return typeMap[type] || 'other'; -}; \ No newline at end of file +}; + +/** + * 获取微信好友列表 + * @param wechatId 微信账号ID + * @param page 页码 + * @param limit 每页数量 + * @param keyword 搜索关键词 + * @returns 好友列表数据 + */ +export const fetchWechatFriends = async ( + wechatId: string | number, + page: number = 1, + limit: number = 20, + keyword: string = "" +): Promise<{ code: number; msg: string; data: any }> => { + try { + const params = new URLSearchParams({ + wechatId: String(wechatId), + page: String(page), + limit: String(limit) + }); + + if (keyword) { + params.append('keyword', keyword); + } + + const url = `/v1/device/wechats/friends?${params.toString()}`; + const response = await api.get<{ code: number; msg: string; data: any }>(url); + + return response; + } catch (error) { + console.error('获取微信好友列表失败:', error); + throw error; + } +} \ No newline at end of file diff --git a/Cunkebao/app/wechat-accounts/[id]/page.tsx b/Cunkebao/app/wechat-accounts/[id]/page.tsx index b9296807..ae5ec158 100755 --- a/Cunkebao/app/wechat-accounts/[id]/page.tsx +++ b/Cunkebao/app/wechat-accounts/[id]/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useState, useEffect } from "react" +import { useState, useEffect, useRef, useCallback } from "react" import { useRouter } from "next/navigation" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" @@ -44,7 +44,7 @@ import { PaginationPrevious, } from "@/components/ui/pagination" import { toast } from "@/components/ui/use-toast" -import { fetchWechatAccountDetail, transformWechatAccountDetail } from "@/api/wechat-accounts" +import { fetchWechatAccountDetail, transformWechatAccountDetail, fetchWechatFriends } from "@/api/wechat-accounts" interface RestrictionRecord { id: string @@ -120,11 +120,274 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri const [showFriendDetail, setShowFriendDetail] = useState(false) const [selectedFriend, setSelectedFriend] = useState(null) const [searchQuery, setSearchQuery] = useState("") - const [currentPage, setCurrentPage] = useState(1) const [activeTab, setActiveTab] = useState("overview") - const friendsPerPage = 10 const [isLoading, setIsLoading] = useState(false) + // 好友列表相关状态 + const [friends, setFriends] = useState([]) + const [friendsPage, setFriendsPage] = useState(1) + const [friendsTotal, setFriendsTotal] = useState(0) + const [hasMoreFriends, setHasMoreFriends] = useState(true) + const [isFetchingFriends, setIsFetchingFriends] = useState(false) + const friendsObserver = useRef(null) + const friendsLoadingRef = useRef(null) + const friendsContainerRef = useRef(null) + + // 计算好友列表容器高度 + const getFriendsContainerHeight = () => { + // 最少显示一条记录的高度,最多显示十条记录的高度 + const minHeight = 80; // 单条记录高度 + const maxHeight = 800; // 十条记录高度 + + if (friends.length === 0) return minHeight; + return Math.min(Math.max(friends.length * 80, minHeight), maxHeight); + }; + + // 生成模拟账号数据(作为备用,服务器请求失败时使用) + const generateMockAccountData = (): WechatAccountDetail => { + // 生成随机标签 + const generateRandomTags = (count: number): FriendTag[] => { + const tagPool = [ + { name: "潜在客户", color: "bg-blue-100 text-blue-800" }, + { name: "高意向", color: "bg-green-100 text-green-800" }, + { name: "已成交", color: "bg-purple-100 text-purple-800" }, + { name: "需跟进", color: "bg-yellow-100 text-yellow-800" }, + { name: "活跃用户", color: "bg-indigo-100 text-indigo-800" }, + { name: "沉默用户", color: "bg-gray-100 text-gray-800" }, + { name: "企业客户", color: "bg-red-100 text-red-800" }, + { name: "个人用户", color: "bg-pink-100 text-pink-800" }, + { name: "新增好友", color: "bg-emerald-100 text-emerald-800" }, + { name: "老客户", color: "bg-amber-100 text-amber-800" }, + ]; + + return Array.from({ length: Math.floor(Math.random() * count) + 1 }, () => { + const randomTag = tagPool[Math.floor(Math.random() * tagPool.length)]; + return { + id: `tag-${Math.random().toString(36).substring(2, 9)}`, + name: randomTag.name, + color: randomTag.color, + }; + }); + }; + + // 生成随机好友 + const friendCount = Math.floor(Math.random() * (300 - 150)) + 150; + const generateFriends = (count: number): WechatFriend[] => { + return Array.from({ length: count }, (_, i) => { + const firstName = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"][Math.floor(Math.random() * 10)]; + const secondName = ["小", "大", "明", "华", "强", "伟", "芳", "娜", "秀", "英"][ + Math.floor(Math.random() * 10) + ]; + const lastName = ["明", "华", "强", "伟", "芳", "娜", "秀", "英", "军", "杰"][Math.floor(Math.random() * 10)]; + const nickname = firstName + secondName + lastName; + + // 生成随机的添加时间(过去1年内) + const addDate = new Date(); + addDate.setDate(addDate.getDate() - Math.floor(Math.random() * 365)); + + // 生成随机的最后互动时间(过去30天内) + const lastDate = new Date(); + lastDate.setDate(lastDate.getDate() - Math.floor(Math.random() * 30)); + + return { + id: `friend-${i}`, + avatar: `/placeholder.svg?height=40&width=40&text=${nickname[0]}`, + nickname, + wechatId: `wxid_${Math.random().toString(36).substring(2, 9)}`, + remark: + Math.random() > 0.5 + ? `${nickname}(${["同事", "客户", "朋友", "同学"][Math.floor(Math.random() * 4)]})` + : "", + addTime: addDate.toISOString().split("T")[0], + lastInteraction: lastDate.toISOString().split("T")[0], + tags: generateRandomTags(3), + region: ["广东", "北京", "上海", "浙江", "江苏", "四川", "湖北", "福建", "山东", "河南"][ + Math.floor(Math.random() * 10) + ], + source: ["抖音", "小红书", "朋友介绍", "搜索添加", "群聊", "附近的人", "名片分享"][ + Math.floor(Math.random() * 7) + ], + notes: + Math.random() > 0.7 + ? ["对产品很感兴趣", "需要进一步跟进", "已购买过产品", "价格敏感", "需要更多信息"][ + Math.floor(Math.random() * 5) + ] + : "", + }; + }); + }; + + const friends = generateFriends(friendCount); + + const mockAccount: WechatAccountDetail = { + id: params.id, + avatar: + "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/img_v3_02jn_e7fcc2a4-3560-478d-911a-4ccd69c6392g.jpg-a8zVtwxMuSrPWN9dfWH93EBY0yM3Dh.jpeg", + nickname: "卡若-25vig", + wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, + deviceId: "device-1", + deviceName: "设备1", + friendCount: friends.length, + todayAdded: 12, + status: "normal", + lastActive: new Date().toLocaleString(), + messageCount: 1234, + activeRate: 87, + accountAge: { + years: 2, + months: 8, + }, + totalChats: 15234, + chatFrequency: 42, + restrictionRecords: [ + { + id: "1", + date: "2024-02-25", + reason: "添加好友过于频繁", + recoveryTime: "2024-02-26", + type: "friend_limit", + }, + { + id: "2", + date: "2024-01-15", + reason: "营销内容违规", + recoveryTime: "2024-01-16", + type: "marketing", + }, + ], + isVerified: true, + firstMomentDate: "2021-06-15", + accountWeight: 85, + weightFactors: { + restrictionFactor: 0.8, + verificationFactor: 1.0, + ageFactor: 0.9, + activityFactor: 0.85, + }, + weeklyStats: Array.from({ length: 7 }, (_, i) => ({ + date: `Day ${i + 1}`, + friends: Math.floor(Math.random() * 50) + 50, + messages: Math.floor(Math.random() * 100) + 100, + })), + friends: friends, + }; + return mockAccount; + }; + + // 随机生成标签颜色 + const getRandomTagColor = (): string => { + const colors = [ + "bg-blue-100 text-blue-800", + "bg-green-100 text-green-800", + "bg-red-100 text-red-800", + "bg-pink-100 text-pink-800", + "bg-emerald-100 text-emerald-800", + "bg-amber-100 text-amber-800", + ]; + return colors[Math.floor(Math.random() * colors.length)]; + }; + + // 获取好友列表数据 + const fetchFriends = useCallback(async (page: number = 1, isNewSearch: boolean = false) => { + if (!account || isFetchingFriends) return; + + try { + setIsFetchingFriends(true); + + // 调用API获取好友列表 + const response = await fetchWechatFriends(account.wechatId, page, 20, searchQuery); + + if (response && response.code === 200) { + const newFriends = response.data.list.map((friend: any) => ({ + id: friend.wechatId, + avatar: friend.avatar, + nickname: friend.nickname || '未设置昵称', + wechatId: friend.wechatId, + remark: friend.remark || '', + addTime: '2024-01-01', // 接口未返回,使用默认值 + lastInteraction: '2024-01-01', // 接口未返回,使用默认值 + tags: (friend.labels || []).map((label: string, index: number) => ({ + id: `tag-${index}`, + name: label, + color: getRandomTagColor(), + })), + region: friend.region || '未知地区', + source: '微信好友', // 接口未返回,使用默认值 + notes: '', + })); + + // 更新状态 + if (isNewSearch) { + setFriends(newFriends); + } else { + setFriends(prev => [...prev, ...newFriends]); + } + + setFriendsTotal(response.data.total); + setFriendsPage(page); + setHasMoreFriends(page * 20 < response.data.total); + } else { + toast({ + title: "获取好友列表失败", + description: response?.msg || "请稍后再试", + variant: "destructive" + }); + } + } catch (error) { + console.error("获取好友列表失败:", error); + toast({ + title: "获取好友列表失败", + description: "请检查网络连接或稍后再试", + variant: "destructive" + }); + } finally { + setIsFetchingFriends(false); + } + }, [account, searchQuery]); + + // 处理搜索 + const handleSearch = useCallback(() => { + setFriends([]); + setFriendsPage(1); + setHasMoreFriends(true); + fetchFriends(1, true); + }, [fetchFriends]); + + // 处理标签切换 + useEffect(() => { + if (activeTab === "friends" && account && friends.length === 0) { + fetchFriends(1, true); + } + }, [activeTab, account, friends.length, fetchFriends]); + + // 设置IntersectionObserver用于懒加载 + useEffect(() => { + friendsObserver.current = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && hasMoreFriends && !isFetchingFriends) { + fetchFriends(friendsPage + 1); + } + }, { threshold: 0.5 }); + + return () => { + if (friendsObserver.current) { + friendsObserver.current.disconnect(); + } + }; + }, [fetchFriends, friendsPage, hasMoreFriends, isFetchingFriends]); + + // 观察加载指示器 + useEffect(() => { + if (friendsLoadingRef.current && friendsObserver.current) { + friendsObserver.current.observe(friendsLoadingRef.current); + } + + return () => { + if (friendsLoadingRef.current && friendsObserver.current) { + friendsObserver.current.unobserve(friendsLoadingRef.current); + } + }; + }, [friendsLoadingRef.current, friendsObserver.current]); + useEffect(() => { // 模拟API调用获取账号详情 const fetchAccount = async () => { @@ -199,23 +462,23 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri } const formatAccountAge = (age: { years: number; months: number }) => { - if (age.years === 0) { - return `${age.months}个月` + if (age.years > 0) { + return `${age.years}年${age.months}个月`; } - if (age.months === 0) { - return `${age.years}年` - } - return `${age.years}年${age.months}个月` - } + return `${age.months}个月`; + }; const handleTransferFriends = () => { setShowTransferConfirm(true) } const confirmTransferFriends = () => { + // 模拟API调用 + toast({ + title: "好友转移成功", + description: `已成功转移 ${account?.friends.length} 个好友`, + }); setShowTransferConfirm(false) - // 跳转到新建计划的订单导入场景 - router.push(`/scenarios/new?type=order&source=${account.wechatId}`) } const handleFriendClick = (friend: WechatFriend) => { @@ -223,150 +486,6 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri setShowFriendDetail(true) } - // 过滤好友 - const filteredFriends = account.friends.filter( - (friend) => - friend.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || - friend.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) || - friend.remark.toLowerCase().includes(searchQuery.toLowerCase()) || - friend.tags.some((tag) => tag.name.toLowerCase().includes(searchQuery.toLowerCase())), - ) - - // 分页 - const totalPages = Math.ceil(filteredFriends.length / friendsPerPage) - const paginatedFriends = filteredFriends.slice((currentPage - 1) * friendsPerPage, currentPage * friendsPerPage) - - // 生成模拟账号数据(作为备用,服务器请求失败时使用) - const generateMockAccountData = () => { - // 生成随机标签 - const generateRandomTags = (count: number) => { - const tagPool = [ - { name: "潜在客户", color: "bg-blue-100 text-blue-800" }, - { name: "高意向", color: "bg-green-100 text-green-800" }, - { name: "已成交", color: "bg-purple-100 text-purple-800" }, - { name: "需跟进", color: "bg-yellow-100 text-yellow-800" }, - { name: "活跃用户", color: "bg-indigo-100 text-indigo-800" }, - { name: "沉默用户", color: "bg-gray-100 text-gray-800" }, - { name: "企业客户", color: "bg-red-100 text-red-800" }, - { name: "个人用户", color: "bg-pink-100 text-pink-800" }, - { name: "新增好友", color: "bg-emerald-100 text-emerald-800" }, - { name: "老客户", color: "bg-amber-100 text-amber-800" }, - ] - - return Array.from({ length: Math.floor(Math.random() * count) + 1 }, () => { - const randomTag = tagPool[Math.floor(Math.random() * tagPool.length)] - return { - id: `tag-${Math.random().toString(36).substring(2, 9)}`, - name: randomTag.name, - color: randomTag.color, - } - }) - } - - // 生成随机好友 - const friendCount = Math.floor(Math.random() * (300 - 150)) + 150 - const generateFriends = (count: number) => { - return Array.from({ length: count }, (_, i) => { - const firstName = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"][Math.floor(Math.random() * 10)] - const secondName = ["小", "大", "明", "华", "强", "伟", "芳", "娜", "秀", "英"][ - Math.floor(Math.random() * 10) - ] - const lastName = ["明", "华", "强", "伟", "芳", "娜", "秀", "英", "军", "杰"][Math.floor(Math.random() * 10)] - const nickname = firstName + secondName + lastName - - // 生成随机的添加时间(过去1年内) - const addDate = new Date() - addDate.setDate(addDate.getDate() - Math.floor(Math.random() * 365)) - - // 生成随机的最后互动时间(过去30天内) - const lastDate = new Date() - lastDate.setDate(lastDate.getDate() - Math.floor(Math.random() * 30)) - - return { - id: `friend-${i}`, - avatar: `/placeholder.svg?height=40&width=40&text=${nickname[0]}`, - nickname, - wechatId: `wxid_${Math.random().toString(36).substring(2, 9)}`, - remark: - Math.random() > 0.5 - ? `${nickname}(${["同事", "客户", "朋友", "同学"][Math.floor(Math.random() * 4)]})` - : "", - addTime: addDate.toISOString().split("T")[0], - lastInteraction: lastDate.toISOString().split("T")[0], - tags: generateRandomTags(3), - region: ["广东", "北京", "上海", "浙江", "江苏", "四川", "湖北", "福建", "山东", "河南"][ - Math.floor(Math.random() * 10) - ], - source: ["抖音", "小红书", "朋友介绍", "搜索添加", "群聊", "附近的人", "名片分享"][ - Math.floor(Math.random() * 7) - ], - notes: - Math.random() > 0.7 - ? ["对产品很感兴趣", "需要进一步跟进", "已购买过产品", "价格敏感", "需要更多信息"][ - Math.floor(Math.random() * 5) - ] - : "", - } - }) - } - - const friends = generateFriends(friendCount) - - const mockAccount: WechatAccountDetail = { - id: params.id, - avatar: - "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/img_v3_02jn_e7fcc2a4-3560-478d-911a-4ccd69c6392g.jpg-a8zVtwxMuSrPWN9dfWH93EBY0yM3Dh.jpeg", - nickname: "卡若-25vig", - wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, - deviceId: "device-1", - deviceName: "设备1", - friendCount: friends.length, - todayAdded: 12, - status: "normal", - lastActive: new Date().toLocaleString(), - messageCount: 1234, - activeRate: 87, - accountAge: { - years: 2, - months: 8, - }, - totalChats: 15234, - chatFrequency: 42, - restrictionRecords: [ - { - id: "1", - date: "2024-02-25", - reason: "添加好友过于频繁", - recoveryTime: "2024-02-26", - type: "friend_limit", - }, - { - id: "2", - date: "2024-01-15", - reason: "营销内容违规", - recoveryTime: "2024-01-16", - type: "marketing", - }, - ], - isVerified: true, - firstMomentDate: "2021-06-15", - accountWeight: 85, - weightFactors: { - restrictionFactor: 0.8, - verificationFactor: 1.0, - ageFactor: 0.9, - activityFactor: 0.85, - }, - weeklyStats: Array.from({ length: 7 }, (_, i) => ({ - date: `Day ${i + 1}`, - friends: Math.floor(Math.random() * 50) + 50, - messages: Math.floor(Math.random() * 100) + 100, - })), - friends: friends, - } - return mockAccount - } - return ( {isLoading ? ( @@ -561,107 +680,82 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri placeholder="搜索好友昵称/微信号/备注/标签" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="pl-9" /> - {/* 好友列表 */} -
- {paginatedFriends.length === 0 ? ( +
+ {friends.length === 0 && !isFetchingFriends ? (
未找到匹配的好友
) : ( - paginatedFriends.map((friend) => ( -
handleFriendClick(friend)} - > - - - {friend.nickname[0]} - -
-
-
- {friend.nickname} - {friend.remark && ({friend.remark})} + <> + {friends.map((friend) => ( +
handleFriendClick(friend)} + > + + + {friend.nickname?.[0] || 'U'} + +
+
+
+ {friend.nickname} + {friend.remark && ({friend.remark})} +
+ +
+
{friend.wechatId}
+
+ {friend.tags.slice(0, 3).map((tag: FriendTag) => ( + + {tag.name} + + ))} + {friend.tags.length > 3 && ( + + +{friend.tags.length - 3} + + )}
- -
-
{friend.wechatId}
-
- {friend.tags.slice(0, 3).map((tag) => ( - - {tag.name} - - ))} - {friend.tags.length > 3 && ( - - +{friend.tags.length - 3} - - )}
-
- )) + ))} + + {/* 懒加载指示器 */} + {hasMoreFriends && ( +
+ {isFetchingFriends && } +
+ )} + )}
- {/* 分页 */} - {totalPages > 1 && ( - - - - { - e.preventDefault() - setCurrentPage((prev) => Math.max(1, prev - 1)) - }} - /> - - {Array.from({ length: Math.min(5, totalPages) }, (_, i) => { - let pageNumber - if (totalPages <= 5) { - pageNumber = i + 1 - } else if (currentPage <= 3) { - pageNumber = i + 1 - } else if (currentPage >= totalPages - 2) { - pageNumber = totalPages - 4 + i - } else { - pageNumber = currentPage - 2 + i - } - return ( - - { - e.preventDefault() - setCurrentPage(pageNumber) - }} - > - {pageNumber} - - - ) - })} - - { - e.preventDefault() - setCurrentPage((prev) => Math.min(totalPages, prev + 1)) - }} - /> - - - - )} + {/* 显示加载状态和总数 */} +
+ {friendsTotal > 0 && ( + + 已加载 {Math.min(friends.length, friendsTotal)} / {friendsTotal} 条记录 + + )} +
@@ -770,7 +864,7 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri 标签
- {selectedFriend.tags.map((tag) => ( + {selectedFriend.tags.map((tag: FriendTag) => ( {tag.name} diff --git a/Server/application/devices/config/route.php b/Server/application/devices/config/route.php index 58190466..05de555b 100755 --- a/Server/application/devices/config/route.php +++ b/Server/application/devices/config/route.php @@ -23,6 +23,7 @@ Route::group('v1/', function () { // 设备微信相关 Route::group('device/wechats', function () { + Route::get('friends', 'app\\devices\\controller\\DeviceWechat@getFriends'); // 获取微信好友列表 Route::get('count', 'app\\devices\\controller\\DeviceWechat@count'); // 获取在线微信账号数量 Route::get('device-count', 'app\\devices\\controller\\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量 Route::get('', 'app\\devices\\controller\\DeviceWechat@index'); // 获取在线微信账号列表 diff --git a/Server/application/devices/controller/DeviceWechat.php b/Server/application/devices/controller/DeviceWechat.php index 49d816f0..545f4706 100755 --- a/Server/application/devices/controller/DeviceWechat.php +++ b/Server/application/devices/controller/DeviceWechat.php @@ -274,23 +274,6 @@ class DeviceWechat extends Controller ] ]; - // 获取微信好友列表 - $friends = Db::table('tk_wechat_friend') - ->where('wechatAccountId', $id) - ->where('isDeleted', 0) - ->field([ - 'id', - 'wechatId', - 'nickname', - 'avatar', - 'gender', - 'region', - 'signature', - 'labels', - 'createTime' - ]) - ->select(); - // 处理返回数据 $data = [ 'basicInfo' => [ @@ -322,7 +305,6 @@ class DeviceWechat extends Controller 'lastUpdateTime' => $wechat['updateTime'] ], 'restrictions' => $restrictions, - 'friends' => $friends ]; return json([ @@ -528,4 +510,73 @@ class DeviceWechat extends Controller ]); } } + + /** + * 获取微信好友列表 + * 根据wechatId查询微信好友,支持分页和关键词筛选 + * + * @return \think\response\Json + */ + public function getFriends() + { + try { + // 获取请求参数 + $wechatId = Request::param('wechatId'); + $page = (int)Request::param('page', 1); + $limit = (int)Request::param('limit', 20); + $keyword = Request::param('keyword', ''); + + // 参数验证 + if (empty($wechatId)) { + return json([ + 'code' => 400, + 'msg' => '参数错误:微信ID不能为空' + ]); + } + + // 查询参数 + $params = []; + if (!empty($keyword)) { + $params['keyword'] = $keyword; + } + + // 调用模型方法获取好友列表 + $result = \app\devices\model\WechatFriend::getFriendsByWechatId($wechatId, $params, $page, $limit); + + + + // 处理返回的数据 + $friendsList = []; + foreach ($result['list'] as $friend) { + $friendsList[] = [ + 'wechatId' => $friend['wechatId'], + 'avatar' => $friend['avatar'] ?: '/placeholder.svg', + 'labels' => $friend['labels'] ?: [], + 'accountNickname' => $friend['accountNickname'] ?: '', + 'accountRealName' => $friend['accountRealName'] ?: '', + 'nickname' => $friend['nickname'] ?: '', + 'remark' => $friend['conRemark'] ?: '', + 'alias' => $friend['alias'] ?: '', + 'gender' => $friend['gender'] ?: 0, + 'region' => $friend['region'] ?: '' + ]; + } + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'total' => $result['total'], + 'page' => $result['page'], + 'limit' => $result['limit'], + 'list' => $friendsList + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } } \ No newline at end of file diff --git a/Server/application/devices/model/WechatFriend.php b/Server/application/devices/model/WechatFriend.php new file mode 100644 index 00000000..c166d922 --- /dev/null +++ b/Server/application/devices/model/WechatFriend.php @@ -0,0 +1,84 @@ + 'integer', + 'wechatAccountId' => 'integer', + 'gender' => 'integer', + 'addFrom' => 'integer', + 'isDeleted' => 'integer', + 'isPassed' => 'integer', + 'accountId' => 'integer', + 'groupId' => 'integer', + 'labels' => 'json', + 'deleteTime' => 'datetime', + 'passTime' => 'datetime', + 'createTime' => 'datetime' + ]; + + /** + * 根据微信账号ID获取好友列表 + * + * @param string $ownerWechatId 所有者微信ID + * @param array $params 查询条件参数 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return array 好友列表和总数 + */ + public static function getFriendsByWechatId($ownerWechatId, $params = [], $page = 1, $limit = 20) + { + // 构建基础查询 + $query = self::where('ownerWechatId', $ownerWechatId) + ->where('isDeleted', 0); + + // 添加筛选条件(昵称、备注、微信号、标签) + if (!empty($params['keyword'])) { + $keyword = $params['keyword']; + $query->where(function($q) use ($keyword) { + $q->whereOr('nickname', 'like', "%{$keyword}%") + ->whereOr('conRemark', 'like', "%{$keyword}%") + ->whereOr('alias', 'like', "%{$keyword}%") + ->whereOr("JSON_SEARCH(labels, 'one', '%{$keyword}%') IS NOT NULL"); + }); + } + + // 计算总数 + $total = $query->count(); + + // 分页查询数据 + $friends = $query->page($page, $limit) + ->order('createTime desc') + ->field('wechatId, alias, avatar, labels, accountNickname, accountRealName, nickname, conRemark, gender, region') + ->select(); + + return [ + 'list' => $friends, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ]; + } +} \ No newline at end of file