diff --git a/Cunkebao/api/wechat-accounts.ts b/Cunkebao/api/wechat-accounts.ts new file mode 100644 index 00000000..1cc6cf9e --- /dev/null +++ b/Cunkebao/api/wechat-accounts.ts @@ -0,0 +1,91 @@ +import { api } from "@/lib/api"; +import { + ServerWechatAccountsResponse, + QueryWechatAccountParams, + WechatAccountDetailResponse +} from "@/types/wechat-account"; + +/** + * 获取微信账号列表 + * @param params 查询参数 + * @returns 微信账号列表响应 + */ +export const fetchWechatAccountList = async (params: QueryWechatAccountParams = {}): Promise => { + const queryParams = new URLSearchParams(); + + // 添加查询参数 + if (params.page) queryParams.append('page', params.page.toString()); + if (params.limit) queryParams.append('limit', params.limit.toString()); + if (params.keyword) queryParams.append('nickname', params.keyword); // 使用nickname作为关键词搜索参数 + if (params.sort) queryParams.append('sort', params.sort); + if (params.order) queryParams.append('order', params.order); + + // 发起API请求 + 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 刷新结果 + */ +export const refreshWechatAccounts = async (): Promise<{ code: number; msg: string; data: any }> => { + return api.put<{ code: number; msg: string; data: any }>('/v1/device/wechats/refresh', {}); +}; + +/** + * 执行微信好友转移 + * @param sourceId 源微信账号ID + * @param targetId 目标微信账号ID + * @returns 转移结果 + */ +export const transferWechatFriends = async (sourceId: string | number, targetId: string | number): Promise<{ code: number; msg: string; data: any }> => { + return api.post<{ code: number; msg: string; data: any }>('/v1/device/wechats/transfer-friends', { + source_id: sourceId, + target_id: targetId + }); +}; + +/** + * 将服务器返回的微信账号数据转换为前端使用的格式 + * @param serverAccount 服务器返回的微信账号数据 + * @returns 前端使用的微信账号数据 + */ +export const transformWechatAccount = (serverAccount: any): import("@/types/wechat-account").WechatAccount => { + // 从deviceInfo中提取设备信息 + let deviceId = ''; + let deviceName = ''; + + if (serverAccount.deviceInfo) { + const deviceInfo = serverAccount.deviceInfo.split(' '); + deviceId = deviceInfo[0] || ''; + deviceName = deviceInfo[1] ? deviceInfo[1].replace(/[()]/g, '') : ''; + } + + // 假设每天最多可添加20个好友 + const maxDailyAdds = 20; + const todayAdded = serverAccount.todayNewFriendCount || 0; + + return { + id: serverAccount.id.toString(), + avatar: serverAccount.avatar || '', + nickname: serverAccount.nickname || serverAccount.accountNickname || '未命名', + wechatId: serverAccount.wechatId || '', + deviceId, + deviceName, + friendCount: serverAccount.totalFriend || 0, + todayAdded, + remainingAdds: serverAccount.canAddFriendCount || (maxDailyAdds - todayAdded), + maxDailyAdds, + status: serverAccount.wechatAlive === 1 ? "normal" : "abnormal" as "normal" | "abnormal", + lastActive: new Date().toLocaleString() // 服务端未提供,使用当前时间 + }; +}; \ No newline at end of file diff --git a/Cunkebao/app/wechat-accounts/page.tsx b/Cunkebao/app/wechat-accounts/page.tsx index 829f206c..3a9b1e91 100644 --- a/Cunkebao/app/wechat-accounts/page.tsx +++ b/Cunkebao/app/wechat-accounts/page.tsx @@ -1,7 +1,7 @@ "use client" -import { useState } from "react" -import { ChevronLeft, Filter, Search, RefreshCw, ArrowRightLeft, AlertCircle } from "lucide-react" +import { useState, useEffect } from "react" +import { ChevronLeft, Filter, Search, RefreshCw, ArrowRightLeft, AlertCircle, Loader2 } from "lucide-react" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -20,73 +20,140 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from " import { toast } from "@/components/ui/use-toast" import { Progress } from "@/components/ui/progress" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" - -interface WechatAccount { - id: string - avatar: string - nickname: string - wechatId: string - deviceId: string - deviceName: string - friendCount: number - todayAdded: number - remainingAdds: number - maxDailyAdds: number - status: "normal" | "abnormal" - lastActive: string -} - -const generateRandomWechatAccounts = (count: number): WechatAccount[] => { - return Array.from({ length: count }, (_, index) => ({ - id: `account-${index + 1}`, - avatar: - "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/img_v3_02jn_e7fcc2a4-3560-478d-911a-4ccd69c6392g.jpg-a8zVtwxMuSrPWN9dfWH93EBY0yM3Dh.jpeg", - nickname: `卡若-${["25vig", "zok7e", "ip9ob", "2kna3"][index % 4]}`, - wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, - deviceId: `device-${Math.floor(index / 3) + 1}`, - deviceName: `设备${Math.floor(index / 3) + 1}`, - friendCount: Math.floor(Math.random() * (6300 - 520)) + 520, - todayAdded: Math.floor(Math.random() * 15), - remainingAdds: Math.floor(Math.random() * 10) + 5, - maxDailyAdds: 20, - status: Math.random() > 0.2 ? "normal" : "abnormal", - lastActive: new Date(Date.now() - Math.random() * 86400000).toLocaleString(), - })) -} +import { fetchWechatAccountList, refreshWechatAccounts, transferWechatFriends, transformWechatAccount } from "@/api/wechat-accounts" +import { WechatAccount } from "@/types/wechat-account" export default function WechatAccountsPage() { const router = useRouter() - const [accounts] = useState(generateRandomWechatAccounts(42)) + const [accounts, setAccounts] = useState([]) const [searchQuery, setSearchQuery] = useState("") const [currentPage, setCurrentPage] = useState(1) const [isTransferDialogOpen, setIsTransferDialogOpen] = useState(false) const [selectedAccount, setSelectedAccount] = useState(null) + const [totalAccounts, setTotalAccounts] = useState(0) + const [isLoading, setIsLoading] = useState(true) + const [isRefreshing, setIsRefreshing] = useState(false) const accountsPerPage = 10 - const filteredAccounts = accounts.filter( - (account) => - account.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || - account.wechatId.toLowerCase().includes(searchQuery.toLowerCase()), - ) + // 获取微信账号列表 + const fetchAccounts = async (page: number = 1, keyword: string = "") => { + try { + setIsLoading(true); + const response = await fetchWechatAccountList({ + page, + limit: accountsPerPage, + keyword, + sort: 'id', + order: 'desc' + }); - const paginatedAccounts = filteredAccounts.slice((currentPage - 1) * accountsPerPage, currentPage * accountsPerPage) + if (response && response.code === 200 && response.data) { + // 转换数据格式 + const wechatAccounts = response.data.list.map(transformWechatAccount); + setAccounts(wechatAccounts); + setTotalAccounts(response.data.total); + } else { + toast({ + title: "获取微信账号失败", + description: response?.msg || "请稍后再试", + variant: "destructive" + }); + // 如果API请求失败,设置空数组 + setAccounts([]); + setTotalAccounts(0); + } + } catch (error) { + console.error("获取微信账号列表失败:", error); + toast({ + title: "获取微信账号失败", + description: "请检查网络连接或稍后再试", + variant: "destructive" + }); + setAccounts([]); + setTotalAccounts(0); + } finally { + setIsLoading(false); + } + }; - const totalPages = Math.ceil(filteredAccounts.length / accountsPerPage) + // 刷新微信账号状态 + const handleRefresh = async () => { + try { + setIsRefreshing(true); + const response = await refreshWechatAccounts(); + + if (response && response.code === 200) { + toast({ + title: "刷新成功", + description: "微信账号状态已更新" + }); + // 重新获取数据 + await fetchAccounts(currentPage, searchQuery); + } else { + toast({ + title: "刷新失败", + description: response?.msg || "请稍后再试", + variant: "destructive" + }); + } + } catch (error) { + console.error("刷新微信账号状态失败:", error); + toast({ + title: "刷新失败", + description: "请检查网络连接或稍后再试", + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + + // 初始加载和页码变化时获取数据 + useEffect(() => { + fetchAccounts(currentPage, searchQuery); + }, [currentPage]); + + // 搜索时重置页码并获取数据 + const handleSearch = () => { + setCurrentPage(1); + fetchAccounts(1, searchQuery); + }; + + // 处理搜索框回车事件 + const handleSearchKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearch(); + } + }; + + const filteredAccounts = accounts; + const totalPages = Math.ceil(totalAccounts / accountsPerPage); const handleTransferFriends = (account: WechatAccount) => { setSelectedAccount(account) setIsTransferDialogOpen(true) } - const handleConfirmTransfer = () => { + const handleConfirmTransfer = async () => { if (!selectedAccount) return - toast({ - title: "好友转移计划已创建", - description: "请在场景获客中查看详情", - }) - setIsTransferDialogOpen(false) - router.push("/scenarios") + try { + // 实际实现好友转移功能,这里需要另一个账号作为目标 + // 现在只是模拟效果 + toast({ + title: "好友转移计划已创建", + description: "请在场景获客中查看详情", + }) + setIsTransferDialogOpen(false) + router.push("/scenarios") + } catch (error) { + console.error("好友转移失败:", error); + toast({ + title: "好友转移失败", + description: "请稍后再试", + variant: "destructive" + }); + } } return ( @@ -110,126 +177,176 @@ export default function WechatAccountsPage() { placeholder="搜索微信号/昵称" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} + onKeyDown={handleSearchKeyDown} /> - -
- {paginatedAccounts.map((account) => ( - router.push(`/wechat-accounts/${account.id}`)} + {isLoading ? ( +
+ +
+ ) : accounts.length === 0 ? ( +
+

暂无微信账号数据

+ -
-
-
微信号:{account.wechatId}
+ {isRefreshing ? ( + + ) : ( + + )} + 刷新 + +
+ ) : ( +
+ {accounts.map((account) => ( + router.push(`/wechat-accounts/${account.id}`)} + > +
+ + + {account.nickname[0]} + +
-
好友数量:{account.friendCount}
-
今日新增:+{account.todayAdded}
-
-
-
-
- 今日可添加: - {account.remainingAdds} - - - - - - -

每日最多添加 {account.maxDailyAdds} 个好友

-
-
-
-
- - {account.todayAdded}/{account.maxDailyAdds} - +
+

{account.nickname}

+ + {account.status === "normal" ? "正常" : "异常"} +
- +
-
-
所属设备:{account.deviceName}
-
最后活跃:{account.lastActive}
+
+
微信号:{account.wechatId}
+
+
好友数量:{account.friendCount}
+
今日新增:+{account.todayAdded}
+
+
+
+
+ 今日可添加: + {account.remainingAdds} + + + + + + +

每日最多添加 {account.maxDailyAdds} 个好友

+
+
+
+
+ + {account.todayAdded}/{account.maxDailyAdds} + +
+ +
+
+
所属设备:{account.deviceName || '未知设备'}
+
最后活跃:{account.lastActive}
+
-
- - ))} -
+
+ ))} +
+ )} -
- - - - { - e.preventDefault() - setCurrentPage((prev) => Math.max(1, prev - 1)) - }} - /> - - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( - - 0 && totalPages > 1 && ( +
+ + + + { e.preventDefault() - setCurrentPage(page) + if (currentPage > 1) { + setCurrentPage((prev) => prev - 1) + } }} - > - {page} - + /> - ))} - - { - e.preventDefault() - setCurrentPage((prev) => Math.min(totalPages, prev + 1)) - }} - /> - - - -
+ {Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { + // 显示当前页附近的页码 + let pageToShow = i + 1; + if (currentPage > 3 && totalPages > 5) { + pageToShow = Math.min(currentPage - 2 + i, totalPages); + if (pageToShow > totalPages - 4) { + pageToShow = totalPages - 4 + i; + } + } + return ( + + { + e.preventDefault() + setCurrentPage(pageToShow) + }} + > + {pageToShow} + + + ); + })} + + { + e.preventDefault() + if (currentPage < totalPages) { + setCurrentPage((prev) => prev + 1) + } + }} + /> + +
+
+
+ )}
diff --git a/Cunkebao/types/wechat-account.ts b/Cunkebao/types/wechat-account.ts new file mode 100644 index 00000000..15ae51cc --- /dev/null +++ b/Cunkebao/types/wechat-account.ts @@ -0,0 +1,113 @@ +// 服务端返回的微信账号数据结构 +export interface ServerWechatAccount { + id: number; + wechatId: string; + nickname: string; + accountNickname: string; + avatar: string; + accountUserName: string; + status: string; + deviceStatus: string; + totalFriend: number; + canAddFriendCount: number; + deviceInfo: string; + todayNewFriendCount: number; + wechatAlive: number; + deviceAlive: number; + imei: string; + deviceMemo: string; +} + +// 服务器响应结构 +export interface ServerWechatAccountsResponse { + code: number; + msg: string; + data: { + total: number; + list: ServerWechatAccount[]; + }; +} + +// 前端使用的微信账号数据结构 +export interface WechatAccount { + id: string; + avatar: string; + nickname: string; + wechatId: string; + deviceId: string; + deviceName: string; + friendCount: number; + todayAdded: number; + remainingAdds: number; + maxDailyAdds: number; + status: "normal" | "abnormal"; + lastActive: string; +} + +// 微信账号查询参数 +export interface QueryWechatAccountParams { + keyword?: string; + page?: number; + limit?: number; + sort?: string; + order?: string; +} + +// 微信好友数据结构 +export interface WechatFriend { + id: string; + wechatId: string; + nickname: string; + avatar: string; + gender: number; + region: string; + signature: string; + labels: string; + createTime: string; +} + +// 微信账号详情数据结构 +export interface WechatAccountDetail { + basicInfo: { + id: number; + wechatId: string; + nickname: string; + avatar: string; + status: string; + deviceStatus: string; + deviceInfo: string; + gender: number; + region: string; + signature: string; + }; + statistics: { + totalFriend: number; + maleFriend: number; + femaleFriend: number; + canAddFriendCount: number; + yesterdayMsgCount: number; + sevenDayMsgCount: number; + thirtyDayMsgCount: number; + }; + accountInfo: { + age: number; + activityLevel: string; + weight: number; + createTime: string; + lastUpdateTime: string; + }; + restrictions: Array<{ + type: string; + reason: string; + startTime: string; + endTime: string; + }>; + friends: WechatFriend[]; +} + +// 微信账号详情响应 +export interface WechatAccountDetailResponse { + code: number; + msg: string; + data: WechatAccountDetail; +} \ No newline at end of file diff --git a/Server/application/common/model/CompanyAccount.php b/Server/application/common/model/CompanyAccount.php new file mode 100644 index 00000000..12964bb7 --- /dev/null +++ b/Server/application/common/model/CompanyAccount.php @@ -0,0 +1,131 @@ + 'integer', + 'tenantId' => 'integer', + 'accountType' => 'integer', + 'companyId' => 'integer', + 'useGoogleSecretKey' => 'boolean', + 'hasVerifyGoogleSecret' => 'boolean', + 'lastLoginTime' => 'integer', + 'createTime' => 'integer', + 'updateTime' => 'integer' + ]; + + /** + * 获取公司账户信息 + * @param string $userName 用户名 + * @param string $password 密码(MD5加密后的) + * @return array|null + */ + public static function getAccount($userName, $password) + { + // 查询账户 + $account = self::where('userName', $userName) + ->find(); + + if (!$account) { + return null; + } + + // 验证密码 + if ($account->passwordMd5 !== $password) { + return null; + } + + // 更新登录信息 + $account->lastLoginIp = request()->ip(); + $account->lastLoginTime = time(); + $account->save(); + + return [ + 'id' => $account->id, + 'tenantId' => $account->tenantId, + 'userName' => $account->userName, + 'realName' => $account->realName, + 'nickname' => $account->nickname, + 'avatar' => $account->avatar, + 'accountType' => $account->accountType, + 'companyId' => $account->companyId, + 'lastLoginIp' => $account->lastLoginIp, + 'lastLoginTime' => $account->lastLoginTime + ]; + } + + /** + * 通过租户ID获取账户信息 + * @param int $companyId 租户ID + * @return array|null + */ + public static function getAccountByCompanyId($companyId) + { + // 查询账户 + $account = self::where('companyId', $companyId)->find(); + + if (!$account) { + return null; + } + + return [ + 'id' => $account->id, + 'tenantId' => $account->tenantId, + 'userName' => $account->userName, + 'realName' => $account->realName, + 'nickname' => $account->nickname, + 'avatar' => $account->avatar, + 'accountType' => $account->accountType, + 'companyId' => $account->companyId, + 'lastLoginIp' => $account->lastLoginIp, + 'lastLoginTime' => $account->lastLoginTime + ]; + } + +} \ No newline at end of file diff --git a/Server/application/devices/controller/DeviceWechat.php b/Server/application/devices/controller/DeviceWechat.php index 5e151423..49d816f0 100644 --- a/Server/application/devices/controller/DeviceWechat.php +++ b/Server/application/devices/controller/DeviceWechat.php @@ -1,6 +1,7 @@ userInfo; // 获取查询条件 $where = []; @@ -102,7 +105,7 @@ class DeviceWechat extends Controller if (!empty($nickname)) { $where['nickname|accountNickname'] = ['like', "%{$nickname}%"]; } - + // 获取分页参数 $page = (int)Request::param('page', 1); $limit = (int)Request::param('limit', 10); @@ -110,22 +113,74 @@ class DeviceWechat extends Controller // 获取排序参数 $sort = Request::param('sort', 'id'); $order = Request::param('order', 'desc'); - - // 获取在线微信账号列表 - $list = WechatAccount::getOnlineWechatList($where, "{$sort} {$order}", $page, $limit); + + // 公司账户表没有 companyId,需要转换一下 + $acountInfo = CompanyAccount::getAccountByCompanyId($userInfo['companyId']); + + // 先用账号进行查询 + $where['accountUserName'] = $acountInfo['userName']; + + // 根据用户权限不同实现不同的查询逻辑 + if ($userInfo['isAdmin'] == 1) { + // 管理员直接查询tk_wechat_account表 + $list = WechatAccount::getOnlineWechatList($where, "{$sort} {$order}", $page, $limit); + } else { + // 非管理员先查询tk_device_user表 + $deviceIds = Db::table('tk_device_user') + ->where('companyId', $userInfo['companyId']) + ->where('userId', $userInfo['id']) + ->column('deviceId'); + + if (empty($deviceIds)) { + // 如果没有绑定设备,返回提示信息 + return json([ + 'code' => 403, + 'msg' => '请联系管理员绑定设备微信', + 'data' => [ + 'total' => 0, + 'list' => [] + ] + ]); + } + + // 获取这些设备关联的微信ID + $wechatIds = Db::table('tk_device_wechat_login') + ->where('companyId', $userInfo['companyId']) + ->whereIn('deviceId', $deviceIds) + ->column('wechatId'); + + if (empty($wechatIds)) { + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'total' => 0, + 'list' => [] + ] + ]); + } + + // 将微信ID添加到查询条件中 + $where['id'] = ['in', $wechatIds]; + + // 查询微信账号 + $list = WechatAccount::getOnlineWechatList($where, "{$sort} {$order}", $page, $limit); + } // 处理返回数据 $data = []; foreach ($list->items() as $item) { // 计算今日可添加好友数量(这里使用一个示例算法,你可以根据实际需求修改) - $canAddFriendCount = 30 - (isset($item['yesterdayMsgCount']) ? intval($item['yesterdayMsgCount']) : 0); + $canAddFriendCount = 20 - Db::table('tk_friend_task')->where('wechatId', $item['wechatId'])->count('*'); if ($canAddFriendCount < 0) { $canAddFriendCount = 0; } - + // 计算今日新增好友数量(示例数据,实际需要从数据库获取或通过其他方式计算) // 这里只是一个示例,你需要根据实际情况替换 - $todayNewFriendCount = mt_rand(0, 10); // 随机生成0-10的数字作为示例 + $todayNewFriendCount = Db::table('tk_friend_task')->where('wechatId', $item['wechatId']) + ->where('status', 3) + ->count('*'); $data[] = [ 'id' => $item['id'], diff --git a/Server/application/devices/model/FriendTask.php b/Server/application/devices/model/FriendTask.php new file mode 100644 index 00000000..899ad0b6 --- /dev/null +++ b/Server/application/devices/model/FriendTask.php @@ -0,0 +1,324 @@ + 'integer', + 'tenantId' => 'integer', + 'operatorAccountId' => 'integer', + 'status' => 'integer', + 'wechatAccountId' => 'integer', + 'createTime' => 'integer', + 'updateTime' => 'integer' + ]; + + /** + * 状态常量 + */ + const STATUS_PENDING = 1; // 待处理 + const STATUS_PROCESSING = 2; // 处理中 + const STATUS_APPROVED = 3; // 已通过 + const STATUS_REJECTED = 4; // 已拒绝 + const STATUS_EXPIRED = 5; // 已过期 + const STATUS_CANCELLED = 6; // 已取消 + + /** + * 获取状态文本 + * @param int $status 状态码 + * @return string 状态文本 + */ + public static function getStatusText($status) + { + $statusMap = [ + self::STATUS_PENDING => '待处理', + self::STATUS_PROCESSING => '处理中', + self::STATUS_APPROVED => '已通过', + self::STATUS_REJECTED => '已拒绝', + self::STATUS_EXPIRED => '已过期', + self::STATUS_CANCELLED => '已取消' + ]; + + return isset($statusMap[$status]) ? $statusMap[$status] : '未知状态'; + } + + /** + * 获取好友任务列表 + * @param array $where 查询条件 + * @param string $order 排序条件 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator + */ + public static function getTaskList($where = [], $order = 'createTime desc', $page = 1, $limit = 10) + { + return self::where($where) + ->order($order) + ->paginate($limit, false, ['page' => $page]); + } + + /** + * 获取任务详情 + * @param int $id 任务ID + * @return array|null + */ + public static function getTaskDetail($id) + { + return self::where('id', $id)->find(); + } + + /** + * 创建好友任务 + * @param array $data 任务数据 + * @return int|bool 任务ID或false + */ + public static function createTask($data) + { + // 确保必填字段存在 + if (!isset($data['id'])) { + return false; + } + + // 设置默认值 + if (!isset($data['status'])) { + $data['status'] = self::STATUS_PENDING; + } + + // 设置创建时间 + $data['createTime'] = time(); + $data['updateTime'] = time(); + + // 创建任务 + $task = new self; + $task->allowField(true)->save($data); + + return $task->id; + } + + /** + * 更新任务信息 + * @param int $id 任务ID + * @param array $data 更新数据 + * @return bool + */ + public static function updateTask($id, $data) + { + // 更新时间 + $data['updateTime'] = time(); + + return self::where('id', $id)->update($data); + } + + /** + * 更新任务状态 + * @param int $id 任务ID + * @param int $status 新状态 + * @param string $remark 备注 + * @return bool + */ + public static function updateTaskStatus($id, $status, $remark = '') + { + $data = [ + 'status' => $status, + 'updateTime' => time() + ]; + + if (!empty($remark)) { + $data['remark'] = $remark; + } + + return self::where('id', $id)->update($data); + } + + /** + * 取消任务 + * @param int $id 任务ID + * @param string $remark 取消原因 + * @return bool + */ + public static function cancelTask($id, $remark = '') + { + return self::updateTaskStatus($id, self::STATUS_CANCELLED, $remark); + } + + /** + * 任务审批通过 + * @param int $id 任务ID + * @param string $remark 备注信息 + * @return bool + */ + public static function approveTask($id, $remark = '') + { + return self::updateTaskStatus($id, self::STATUS_APPROVED, $remark); + } + + /** + * 任务拒绝 + * @param int $id 任务ID + * @param string $remark 拒绝原因 + * @return bool + */ + public static function rejectTask($id, $remark = '') + { + return self::updateTaskStatus($id, self::STATUS_REJECTED, $remark); + } + + /** + * 根据微信账号ID获取任务列表 + * @param int $wechatAccountId 微信账号ID + * @param array $status 状态数组,默认查询所有状态 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator + */ + public static function getTasksByWechatAccount($wechatAccountId, $status = [], $page = 1, $limit = 10) + { + $where = ['wechatAccountId' => $wechatAccountId]; + + if (!empty($status)) { + $where['status'] = ['in', $status]; + } + + return self::getTaskList($where, 'createTime desc', $page, $limit); + } + + /** + * 根据操作账号ID获取任务列表 + * @param int $operatorAccountId 操作账号ID + * @param array $status 状态数组,默认查询所有状态 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator + */ + public static function getTasksByOperator($operatorAccountId, $status = [], $page = 1, $limit = 10) + { + $where = ['operatorAccountId' => $operatorAccountId]; + + if (!empty($status)) { + $where['status'] = ['in', $status]; + } + + return self::getTaskList($where, 'createTime desc', $page, $limit); + } + + /** + * 根据手机号/微信号查询任务 + * @param string $phone 手机号/微信号 + * @param int $tenantId 租户ID + * @return array + */ + public static function getTasksByPhone($phone, $tenantId = null) + { + $where = ['phone' => $phone]; + + if ($tenantId !== null) { + $where['tenantId'] = $tenantId; + } + + return self::where($where)->select(); + } + + /** + * 获取统计数据 + * @param int $tenantId 租户ID + * @param int $timeRange 时间范围(秒) + * @return array + */ + public static function getTaskStats($tenantId = null, $timeRange = 86400) + { + $where = []; + + if ($tenantId !== null) { + $where['tenantId'] = $tenantId; + } + + // 时间范围 + $startTime = time() - $timeRange; + $where['createTime'] = ['>=', $startTime]; + + // 获取各状态的任务数量 + $stats = [ + 'total' => self::where($where)->count(), + 'pending' => self::where(array_merge($where, ['status' => self::STATUS_PENDING]))->count(), + 'processing' => self::where(array_merge($where, ['status' => self::STATUS_PROCESSING]))->count(), + 'approved' => self::where(array_merge($where, ['status' => self::STATUS_APPROVED]))->count(), + 'rejected' => self::where(array_merge($where, ['status' => self::STATUS_REJECTED]))->count(), + 'expired' => self::where(array_merge($where, ['status' => self::STATUS_EXPIRED]))->count(), + 'cancelled' => self::where(array_merge($where, ['status' => self::STATUS_CANCELLED]))->count() + ]; + + return $stats; + } + + /** + * 任务处理结果统计 + * @param int $tenantId 租户ID + * @param int $timeRange 时间范围(秒) + * @return array + */ + public static function getTaskResultStats($tenantId = null, $timeRange = 86400 * 30) + { + $where = []; + + if ($tenantId !== null) { + $where['tenantId'] = $tenantId; + } + + // 时间范围 + $startTime = time() - $timeRange; + $where['createTime'] = ['>=', $startTime]; + + // 获取处理结果数据 + $stats = [ + 'total' => self::where($where)->count(), + 'approved' => self::where(array_merge($where, ['status' => self::STATUS_APPROVED]))->count(), + 'rejected' => self::where(array_merge($where, ['status' => self::STATUS_REJECTED]))->count(), + 'pending' => self::where(array_merge($where, ['status' => ['in', [self::STATUS_PENDING, self::STATUS_PROCESSING]]]))->count(), + 'other' => self::where(array_merge($where, ['status' => ['in', [self::STATUS_EXPIRED, self::STATUS_CANCELLED]]]))->count() + ]; + + // 计算成功率 + $stats['approvalRate'] = $stats['total'] > 0 ? round($stats['approved'] / $stats['total'] * 100, 2) : 0; + + return $stats; + } +} \ No newline at end of file diff --git a/Server/application/devices/model/WechatAccount.php b/Server/application/devices/model/WechatAccount.php index 16dc8c47..11aaaf78 100644 --- a/Server/application/devices/model/WechatAccount.php +++ b/Server/application/devices/model/WechatAccount.php @@ -97,8 +97,6 @@ class WechatAccount extends Model public static function getOnlineWechatList($where = [], $order = 'id desc', $page = 1, $limit = 10) { $condition = [ - 'wechatAlive' => 1, - 'deviceAlive' => 1, 'isDeleted' => 0 ];