From bd9f070195e3c2606e92fe4a931066571eba5b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Mon, 12 May 2025 11:58:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=81=E5=9F=9F=E6=93=8D=E7=9B=98=E6=89=8B?= =?UTF-8?q?=20-=20=E8=B4=A6=E5=8F=B7=E8=AF=A6=E6=83=85/=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E6=A6=82=E8=A7=88=20=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/api/wechat-accounts.ts | 224 +++--------- Cunkebao/app/wechat-accounts/[id]/page.tsx | 342 +++++++++++------- Server/application/cunkebao/config/route.php | 2 +- ...GetWechatOnDeviceSummarizeV1Controller.php | 6 +- 4 files changed, 261 insertions(+), 313 deletions(-) diff --git a/Cunkebao/api/wechat-accounts.ts b/Cunkebao/api/wechat-accounts.ts index 0f761498..df4a654e 100644 --- a/Cunkebao/api/wechat-accounts.ts +++ b/Cunkebao/api/wechat-accounts.ts @@ -2,9 +2,40 @@ import { api } from "@/lib/api"; import { ServerWechatAccountsResponse, QueryWechatAccountParams, - WechatAccountDetailResponse } from "@/types/wechat-account"; +// 添加接口返回数据类型定义 +interface WechatAccountSummary { + accountAge: string; + activityLevel: { + allTimes: number; + dayTimes: number; + }; + accountWeight: { + scope: number; + ageWeight: number; + activityWeigth: number; + restrictWeight: number; + realNameWeight: number; + }; + statistics: { + todayAdded: number; + addLimit: number; + }; + restrictions: { + id: number; + level: string; + reason: string; + date: string; + }[]; +} + +interface WechatAccountSummaryResponse { + code: number; + msg: string; + data: WechatAccountSummary; +} + /** * 获取微信账号列表 * @param params 查询参数 @@ -24,15 +55,6 @@ export const fetchWechatAccountList = async (params: QueryWechatAccountParams = return api.get(`/v1/device/wechats?${queryParams.toString()}`); }; -/** - * 获取微信账号详情 - * @param id 微信账号ID - * @returns 微信账号详情响应 - */ -export const fetchWechatAccountDetail = async (id: string | number): Promise => { - return api.get(`/v1/device/wechats/${id}`); -}; - /** * 刷新微信账号状态 * @returns 刷新结果 @@ -115,173 +137,35 @@ export const transformWechatAccount = (serverAccount: any): import("@/types/wech }; }; -/** - * 将服务端的微信账号详情转换为前端详情页面所需的格式 - * @param detailResponse 服务端微信账号详情响应 - * @returns 前端页面所需的微信账号详情格式 - */ -export const transformWechatAccountDetail = (detailResponse: WechatAccountDetailResponse): any => { - if (!detailResponse || !detailResponse.data) { - return null; - } - - const { basicInfo, statistics, accountInfo, restrictions, friends } = detailResponse.data; - - // 设备信息处理 - 改进处理方式 - let deviceId = ''; - let deviceName = ''; - - if (basicInfo.deviceInfo) { - // 尝试解析设备信息字符串 - const deviceInfoParts = basicInfo.deviceInfo.split(' '); - if (deviceInfoParts.length > 0) { - // 提取数字部分作为设备ID,确保是整数 - const possibleId = deviceInfoParts[0].trim(); - // 验证是否为数字 - deviceId = /^\d+$/.test(possibleId) ? possibleId : ''; - - // 提取设备名称 - if (deviceInfoParts.length > 1) { - deviceName = deviceInfoParts[1].replace(/[()]/g, '').trim(); - } - } - } - - // 如果从deviceInfo无法获取有效的设备ID,直接使用微信账号ID作为备选 - if (!deviceId && basicInfo.id) { - deviceId = basicInfo.id.toString(); - } - - // 如果没有设备名称,使用备用名称 - if (!deviceName) { - deviceName = '未命名设备'; - } - - // 账号年龄计算 - let accountAgeYears = 0; - let accountAgeMonths = 0; - - if (accountInfo.createTime) { - const createDate = new Date(accountInfo.createTime); - const currentDate = new Date(); - const diffInMonths = (currentDate.getFullYear() - createDate.getFullYear()) * 12 + - (currentDate.getMonth() - createDate.getMonth()); - - accountAgeYears = Math.floor(diffInMonths / 12); - accountAgeMonths = diffInMonths % 12; - } - - // 转换限制记录 - const restrictionRecords = restrictions?.map((restriction, index) => ({ - id: `${index}`, - date: restriction.startTime, - reason: restriction.reason, - recoveryTime: restriction.endTime, - type: mapRestrictionType(restriction.type) - })) || []; - - // 转换好友数据 - const transformedFriends = friends?.map(friend => ({ - id: friend.id.toString(), - avatar: friend.avatar || `/placeholder.svg?height=40&width=40&text=${friend.nickname?.[0] || ''}`, - nickname: friend.nickname, - wechatId: friend.wechatId, - remark: '', // 服务端未提供 - addTime: friend.createTime, - lastInteraction: '', // 服务端未提供 - tags: [], // 服务端未提供 - region: friend.region || '', - source: '', // 服务端未提供 - notes: '', // 服务端未提供 - })) || []; - - // 创建每周统计数据(模拟数据,服务端未提供) - const weeklyStats = Array.from({ length: 7 }, (_, i) => ({ - date: `Day ${i + 1}`, - friends: Math.floor(Math.random() * 50) + 50, - messages: Math.floor(Math.random() * 100) + 100, - })); - - return { - id: basicInfo.id.toString(), - avatar: basicInfo.avatar || '', - nickname: basicInfo.nickname || '', - wechatId: basicInfo.wechatId || '', - deviceId, - deviceName, - friendCount: statistics.totalFriend || 0, - todayAdded: 0, // 服务端未提供,默认为0 - status: basicInfo.status === '在线' ? 'normal' : 'abnormal', - lastActive: accountInfo.lastUpdateTime || new Date().toLocaleString(), - messageCount: statistics.thirtyDayMsgCount || 0, - activeRate: 0, // 服务端未提供,默认为0 - accountAge: { - years: accountAgeYears, - months: accountAgeMonths, - }, - totalChats: statistics.sevenDayMsgCount + statistics.yesterdayMsgCount || 0, - chatFrequency: Math.floor((statistics.sevenDayMsgCount || 0) / 7), // 每日平均聊天次数 - restrictionRecords, - isVerified: true, // 服务端未提供,默认为true - firstMomentDate: accountInfo.createTime || '', - accountWeight: accountInfo.weight || 50, - weightFactors: { - restrictionFactor: restrictionRecords.length > 0 ? 0.8 : 1.0, - verificationFactor: 1.0, - ageFactor: Math.min(1.0, accountAgeYears * 0.1 + 0.5), - activityFactor: statistics.totalFriend > 0 ? 0.9 : 0.7, - }, - weeklyStats, - friends: transformedFriends, - }; -}; - -/** - * 将服务端的限制类型映射为前端类型 - * @param type 服务端限制类型 - * @returns 前端限制类型 - */ -const mapRestrictionType = (type: string): "friend_limit" | "marketing" | "spam" | "other" => { - const typeMap: Record = { - 'friend': 'friend_limit', - 'marketing': 'marketing', - 'spam': 'spam' - }; - - return typeMap[type] || 'other'; -}; - /** * 获取微信好友列表 * @param wechatId 微信账号ID * @param page 页码 - * @param limit 每页数量 - * @param keyword 搜索关键词 + * @param pageSize 每页数量 + * @param searchQuery 搜索关键词 * @returns 好友列表数据 */ -export const fetchWechatFriends = async ( - wechatId: string | number, - page: number = 1, - limit: number = 20, - keyword: string = "" -): Promise<{ code: number; msg: string; data: any }> => { +export const fetchWechatFriends = async (wechatId: string, page: number = 1, pageSize: number = 20, searchQuery: string = '') => { 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; + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/device/wechats/${wechatId}/friends?page=${page}&pageSize=${pageSize}&search=${searchQuery}`); + const data = await response.json(); + return data; } catch (error) { - console.error('获取微信好友列表失败:', error); + console.error("获取好友列表失败:", error); throw error; } -} \ No newline at end of file +}; + +/** + * 获取微信账号概览信息 + * @param id 微信账号ID + * @returns 微信账号概览信息 + */ +export const fetchWechatAccountSummary = async (id: string): Promise => { + try { + return api.get(`/v1/device/wechats/${id}/summary`); + } 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 708d6ce0..9d594436 100644 --- a/Cunkebao/app/wechat-accounts/[id]/page.tsx +++ b/Cunkebao/app/wechat-accounts/[id]/page.tsx @@ -44,7 +44,7 @@ import { PaginationPrevious, } from "@/components/ui/pagination" import { toast } from "@/components/ui/use-toast" -import { fetchWechatAccountDetail, transformWechatAccountDetail, fetchWechatFriends } from "@/api/wechat-accounts" +import { fetchWechatFriends, fetchWechatAccountSummary } from "@/api/wechat-accounts" interface RestrictionRecord { id: string @@ -87,13 +87,12 @@ interface WechatAccountDetail { lastActive: string messageCount: number activeRate: number - // 新增和修改的字段 accountAge: { years: number months: number } totalChats: number - chatFrequency: number // 每日平均聊天次数 + chatFrequency: number restrictionRecords: RestrictionRecord[] isVerified: boolean firstMomentDate: string @@ -112,9 +111,35 @@ interface WechatAccountDetail { friends: WechatFriend[] } +interface WechatAccountSummary { + accountAge: string; + activityLevel: { + allTimes: number; + dayTimes: number; + }; + accountWeight: { + scope: number; + ageWeight: number; + activityWeigth: number; + restrictWeight: number; + realNameWeight: number; + }; + statistics: { + todayAdded: number; + addLimit: number; + }; + restrictions: { + id: number; + level: string; + reason: string; + date: string; + }[]; +} + export default function WechatAccountDetailPage({ params }: { params: { id: string } }) { const router = useRouter() const [account, setAccount] = useState(null) + const [accountSummary, setAccountSummary] = useState(null) const [showRestrictions, setShowRestrictions] = useState(false) const [showTransferConfirm, setShowTransferConfirm] = useState(false) const [showFriendDetail, setShowFriendDetail] = useState(false) @@ -151,9 +176,28 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri try { const decodedData = JSON.parse(decodeURIComponent(dataParam)); setInitialData(decodedData); + // 使用初始数据设置account + const mockData = generateMockAccountData(); + if (decodedData) { + mockData.avatar = decodedData.avatar; + mockData.nickname = decodedData.nickname; + mockData.status = decodedData.status; + mockData.wechatId = decodedData.wechatId; + mockData.deviceName = decodedData.deviceName; + } + setAccount(mockData); + setFriendsTotal(mockData.friendCount); + setIsLoading(false); } catch (error) { console.error('解析初始数据失败:', error); + setIsLoading(false); } + } else { + // 如果没有初始数据,使用模拟数据 + const mockData = generateMockAccountData(); + setAccount(mockData); + setFriendsTotal(mockData.friendCount); + setIsLoading(false); } }, []); @@ -422,79 +466,73 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri }; }, [friendsLoadingRef.current, friendsObserver.current]); - useEffect(() => { - // 模拟API调用获取账号详情 - const fetchAccount = async () => { - try { - setIsLoading(true) - - // 调用API获取微信账号详情 - const response = await fetchWechatAccountDetail(params.id) - - if (response && response.code === 200) { - // 转换数据格式 - const transformedAccount = transformWechatAccountDetail(response) - // 使用初始数据覆盖API返回的部分字段 - if (initialData) { - transformedAccount.avatar = initialData.avatar; - transformedAccount.nickname = initialData.nickname; - transformedAccount.status = initialData.status; - transformedAccount.wechatId = initialData.wechatId; - transformedAccount.deviceName = initialData.deviceName; - } - setAccount(transformedAccount) - - // 如果有好友总数,更新friendsTotal状态 - if (transformedAccount && transformedAccount.friendCount > 0) { - setFriendsTotal(transformedAccount.friendCount); - } - } else { - toast({ - title: "获取微信账号详情失败", - description: response?.msg || "请稍后再试", - variant: "destructive" - }) - // 获取失败时使用模拟数据 - const mockData = generateMockAccountData(); - // 使用初始数据覆盖模拟数据的部分字段 - if (initialData) { - mockData.avatar = initialData.avatar; - mockData.nickname = initialData.nickname; - mockData.status = initialData.status; - mockData.wechatId = initialData.wechatId; - mockData.deviceName = initialData.deviceName; - } - setAccount(mockData); - // 更新好友总数 - setFriendsTotal(mockData.friendCount); - } - } catch (error) { - console.error("获取微信账号详情失败:", error) - toast({ - title: "获取微信账号详情失败", - description: "请检查网络连接或稍后再试", - variant: "destructive" - }) - // 请求出错时使用模拟数据 - const mockData = generateMockAccountData(); - // 使用初始数据覆盖模拟数据的部分字段 - if (initialData) { - mockData.avatar = initialData.avatar; - mockData.nickname = initialData.nickname; - mockData.status = initialData.status; - mockData.wechatId = initialData.wechatId; - mockData.deviceName = initialData.deviceName; - } - setAccount(mockData); - // 更新好友总数 - setFriendsTotal(mockData.friendCount); - } finally { - setIsLoading(false) - } + // 计算账号年龄 + const calculateAccountAge = (registerTime: string) => { + const register = new Date(registerTime); + const now = new Date(); + const years = now.getFullYear() - register.getFullYear(); + const months = now.getMonth() - register.getMonth(); + + if (months < 0) { + return { + years: years - 1, + months: months + 12 + }; } + + return { + years, + months + }; + }; - fetchAccount() - }, [params.id, initialData]) + // 获取账号概览数据 + const fetchSummaryData = useCallback(async () => { + try { + setIsLoading(true); + const response = await fetchWechatAccountSummary(params.id); + if (response.code === 200) { + setAccountSummary(response.data); + } else { + toast({ + title: "获取账号概览失败", + description: response.msg || "请稍后再试", + variant: "destructive" + }); + } + } catch (error) { + console.error("获取账号概览失败:", error); + toast({ + title: "获取账号概览失败", + description: "请检查网络连接或稍后再试", + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }, [params.id]); + + // 在页面加载和切换到概览标签时获取数据 + useEffect(() => { + if (activeTab === "overview") { + fetchSummaryData(); + } + }, [activeTab, fetchSummaryData]); + + // 在初始加载时获取数据 + useEffect(() => { + if (activeTab === "overview") { + fetchSummaryData(); + } + }, [fetchSummaryData, activeTab]); + + // 处理标签切换 + const handleTabChange = (value: string) => { + setActiveTab(value); + if (value === "overview") { + fetchSummaryData(); + } + }; if (!account) { return
加载中...
@@ -618,7 +656,7 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri - + 账号概览 @@ -634,8 +672,16 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri 账号年龄 -
{formatAccountAge(account.accountAge)}
-
注册时间:{account.firstMomentDate}
+ {accountSummary && ( + <> +
+ {formatAccountAge(calculateAccountAge(accountSummary.accountAge))} +
+
+ 注册时间:{new Date(accountSummary.accountAge).toLocaleDateString()} +
+ + )} @@ -643,8 +689,12 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri 活跃程度 -
{account.chatFrequency}次/天
-
总聊天数:{account.totalChats.toLocaleString()}
+ {accountSummary && ( + <> +
{accountSummary.activityLevel.dayTimes}次/天
+
总聊天数:{accountSummary.activityLevel.allTimes.toLocaleString()}
+ + )}
@@ -655,34 +705,40 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri 账号权重评估 -
- {account.accountWeight} - -
- -

{getWeightDescription(account.accountWeight)}

-
-
- 账号年龄 - - {(account.weightFactors.ageFactor * 100).toFixed(0)}% -
-
- 活跃度 - - {(account.weightFactors.activityFactor * 100).toFixed(0)}% -
-
- 限制影响 - - {(account.weightFactors.restrictionFactor * 100).toFixed(0)}% -
-
- 实名认证 - - {(account.weightFactors.verificationFactor * 100).toFixed(0)}% -
+ {accountSummary && ( +
+ {accountSummary.accountWeight.scope} + +
+ )}
+ {accountSummary && ( + <> +

{getWeightDescription(accountSummary.accountWeight.scope)}

+
+
+ 账号年龄 + + {accountSummary.accountWeight.ageWeight}% +
+
+ 活跃度 + + {accountSummary.accountWeight.activityWeigth}% +
+
+ 限制影响 + + {accountSummary.accountWeight.restrictWeight}% +
+
+ 实名认证 + + {accountSummary.accountWeight.realNameWeight}% +
+
+ + )} {/* 添加好友统计 */} @@ -701,29 +757,31 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri -
-
- 今日已添加 - {account.todayAdded} -
-
-
- 添加进度 - - {account.todayAdded}/{calculateMaxDailyAdds(account.accountWeight)} - + {accountSummary && ( +
+
+ 今日已添加 + {accountSummary.statistics.todayAdded} +
+
+
+ 添加进度 + + {accountSummary.statistics.todayAdded}/{accountSummary.statistics.addLimit} + +
+ +
+
+ 根据当前账号权重({accountSummary.accountWeight.scope}分),每日最多可添加{" "} + {accountSummary.statistics.addLimit}{" "} + 个好友
-
-
- 根据当前账号权重({account.accountWeight}分),每日最多可添加{" "} - {calculateMaxDailyAdds(account.accountWeight)}{" "} - 个好友 -
-
+ )} {/* 限制记录 */} @@ -733,20 +791,26 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri 限制记录
- setShowRestrictions(true)}> - 共 {account.restrictionRecords.length} 次 - + {accountSummary && ( + setShowRestrictions(true)}> + 共 {accountSummary.restrictions.length} 次 + + )}
-
- {account.restrictionRecords.slice(0, 2).map((record) => ( -
-
- {record.reason} - {record.date} + {accountSummary && ( +
+ {accountSummary.restrictions.slice(0, 2).map((record) => ( +
+
+ + {record.reason} + + {new Date(record.date).toLocaleDateString()} +
-
- ))} -
+ ))} +
+ )} diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index d53bf622..3df988aa 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -23,7 +23,7 @@ Route::group('v1/', function () { // 设备微信相关 Route::group('device/wechats', function () { Route::get('', 'app\cunkebao\controller\wechat\GetWechatsOnDevicesV1Controller@index'); // 获取在线微信账号列表 - Route::get(':id', 'app\cunkebao\controller\wechat\GetWechatOnDeviceSummarizeV1Controller@index'); // 获取微信号详情 + Route::get(':id/summary', 'app\cunkebao\controller\wechat\GetWechatOnDeviceSummarizeV1Controller@index'); // 获取微信号详情 Route::get('friends', 'app\cunkebao\controller\DeviceWechat@getFriends'); // 获取微信好友列表 Route::get('count', 'app\cunkebao\controller\DeviceWechat@count'); // 获取在线微信账号数量 diff --git a/Server/application/cunkebao/controller/wechat/GetWechatOnDeviceSummarizeV1Controller.php b/Server/application/cunkebao/controller/wechat/GetWechatOnDeviceSummarizeV1Controller.php index bd4feffd..282a024e 100644 --- a/Server/application/cunkebao/controller/wechat/GetWechatOnDeviceSummarizeV1Controller.php +++ b/Server/application/cunkebao/controller/wechat/GetWechatOnDeviceSummarizeV1Controller.php @@ -70,13 +70,13 @@ class GetWechatOnDeviceSummarizeV1Controller extends BaseController return [ [ 'id' => 1, - 'type' => 'warnnig', + 'level' => 2, 'reason' => '频繁添加好友', 'date' => date('Y-m-d H:i:s', strtotime('-1 day')), ], [ 'id' => 2, - 'type' => 'error', + 'level' => 3, 'reason' => '营销内容违规', 'date' => date('Y-m-d H:i:s', strtotime('-1 day')), ], @@ -228,7 +228,7 @@ class GetWechatOnDeviceSummarizeV1Controller extends BaseController protected function getStatistics(string $wechatId, array $accountWeight): array { return [ - 'addedCount' => $this->getTodayNewFriendCount($wechatId), + 'todayAdded' => $this->getTodayNewFriendCount($wechatId), 'addLimit' => $this->_calAllowedFriends($accountWeight['scope']) ]; }