From 6638fcb8a1598229fee95456e971854b906f344a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Tue, 1 Apr 2025 10:00:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=85=B3=E8=81=94=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/api/devices.ts | 5 + Cunkebao/app/devices/[id]/page.tsx | 165 +++++-- Cunkebao/app/devices/page.tsx | 404 +++++++++++++++++- Server/application/devices/config/route.php | 3 + .../application/devices/controller/Device.php | 89 +++- .../devices/model/DeviceWechatLogin.php | 207 +++++++++ 6 files changed, 810 insertions(+), 63 deletions(-) create mode 100644 Server/application/devices/model/DeviceWechatLogin.php diff --git a/Cunkebao/api/devices.ts b/Cunkebao/api/devices.ts index e07f0cfc..e121be43 100644 --- a/Cunkebao/api/devices.ts +++ b/Cunkebao/api/devices.ts @@ -33,6 +33,11 @@ export const fetchDeviceDetail = async (id: string | number): Promise>(`/v1/devices/${id}`); }; +// 获取设备关联的微信账号 +export const fetchDeviceRelatedAccounts = async (id: string | number): Promise> => { + return api.get>(`/v1/devices/${id}/related-accounts`); +}; + // 更新设备任务配置 export const updateDeviceTaskConfig = async ( id: string | number, diff --git a/Cunkebao/app/devices/[id]/page.tsx b/Cunkebao/app/devices/[id]/page.tsx index b0107eff..86a629e8 100644 --- a/Cunkebao/app/devices/[id]/page.tsx +++ b/Cunkebao/app/devices/[id]/page.tsx @@ -4,13 +4,13 @@ import { useState, useEffect } from "react" import { useParams, useRouter } from "next/navigation" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" -import { ChevronLeft, Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History } from "lucide-react" +import { ChevronLeft, Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History, RefreshCw } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Switch } from "@/components/ui/switch" import { Label } from "@/components/ui/label" import { ScrollArea } from "@/components/ui/scroll-area" -import { fetchDeviceDetail, updateDeviceTaskConfig } from "@/api/devices" +import { fetchDeviceDetail, fetchDeviceRelatedAccounts, updateDeviceTaskConfig } from "@/api/devices" import { toast } from "sonner" interface WechatAccount { @@ -18,10 +18,13 @@ interface WechatAccount { avatar: string nickname: string wechatId: string - gender: "male" | "female" - status: "normal" | "abnormal" - addFriendStatus: "enabled" | "disabled" - friendCount: number + gender: number + status: number + statusText: string + wechatAlive: number + wechatAliveText: string + addFriendStatus: number + totalFriend: number lastActive: string } @@ -68,6 +71,7 @@ export default function DeviceDetailPage() { const [device, setDevice] = useState(null) const [activeTab, setActiveTab] = useState("info") const [loading, setLoading] = useState(true) + const [accountsLoading, setAccountsLoading] = useState(false) const [savingFeatures, setSavingFeatures] = useState({ autoAddFriend: false, autoReply: false, @@ -146,16 +150,24 @@ export default function DeviceDetailPage() { avatar: "/placeholder.svg", // 默认头像 nickname: serverData.memo || "微信账号", wechatId: serverData.imei || "", - gender: "male", // 默认性别 - status: serverData.alive === 1 ? "normal" : "abnormal", - addFriendStatus: "enabled", - friendCount: serverData.totalFriend || 0, + gender: 1, // 默认性别 + status: serverData.alive === 1 ? 1 : 0, + statusText: serverData.alive === 1 ? "可加友" : "已停用", + wechatAlive: serverData.alive === 1 ? 1 : 0, + wechatAliveText: serverData.alive === 1 ? "正常" : "异常", + addFriendStatus: 1, + totalFriend: serverData.totalFriend || 0, lastActive: serverData.lastUpdateTime || new Date().toISOString() } ] } setDevice(formattedDevice) + + // 如果当前激活标签是"accounts",则加载关联微信账号 + if (activeTab === "accounts") { + fetchRelatedAccounts() + } } else { // 如果API返回错误,则使用备用模拟数据 toast.error("获取设备信息失败,显示备用数据") @@ -185,10 +197,13 @@ export default function DeviceDetailPage() { avatar: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-q2rVrFbfDdAbSnT3ZTNE7gfn3QCbvr.png", nickname: "老张", wechatId: "wxid_abc123", - gender: "male", - status: "normal", - addFriendStatus: "enabled", - friendCount: 523, + gender: 1, + status: 1, + statusText: "可加友", + wechatAlive: 1, + wechatAliveText: "正常", + addFriendStatus: 1, + totalFriend: 523, lastActive: "2024-02-09 15:20:33", }, { @@ -196,10 +211,13 @@ export default function DeviceDetailPage() { avatar: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-q2rVrFbfDdAbSnT3ZTNE7gfn3QCbvr.png", nickname: "老李", wechatId: "wxid_xyz789", - gender: "male", - status: "abnormal", - addFriendStatus: "disabled", - friendCount: 245, + gender: 1, + status: 0, + statusText: "已停用", + wechatAlive: 0, + wechatAliveText: "异常", + addFriendStatus: 0, + totalFriend: 245, lastActive: "2024-02-09 14:15:22", }, ], @@ -228,7 +246,53 @@ export default function DeviceDetailPage() { } fetchDevice() - }, [params.id]) + }, [params.id, activeTab]) + + // 获取设备关联微信账号 + const fetchRelatedAccounts = async () => { + if (!params.id || accountsLoading) return + + try { + setAccountsLoading(true) + const response = await fetchDeviceRelatedAccounts(params.id as string) + + if (response && response.code === 200 && response.data) { + const accounts = response.data.accounts || [] + + // 更新设备的微信账号信息 + setDevice(prev => { + if (!prev) return null + return { + ...prev, + wechatAccounts: accounts + } + }) + + if (accounts.length > 0) { + toast.success(`成功获取${accounts.length}个关联微信账号`) + } else { + toast.info("此设备暂无关联微信账号") + } + } else { + toast.error("获取关联微信账号失败") + } + } catch (error) { + console.error("获取关联微信账号失败:", error) + toast.error("获取关联微信账号出错") + } finally { + setAccountsLoading(false) + } + } + + // 处理标签页切换 + const handleTabChange = (value: string) => { + setActiveTab(value) + + // 当切换到"关联账号"标签时,获取最新的关联微信账号信息 + if (value === "accounts") { + fetchRelatedAccounts() + } + } // 处理功能开关状态变化 const handleFeatureChange = async (feature: keyof Device['features'], checked: boolean) => { @@ -351,7 +415,7 @@ export default function DeviceDetailPage() {
最后活跃:{device.lastActive}
- + 基本信息 关联账号 @@ -435,8 +499,37 @@ export default function DeviceDetailPage() { +
+

微信账号列表

+ +
+ - {device.wechatAccounts && device.wechatAccounts.length > 0 ? ( + {accountsLoading && ( +
+
+ 加载微信账号中... +
+ )} + + {!accountsLoading && device.wechatAccounts && device.wechatAccounts.length > 0 ? (
{device.wechatAccounts.map((account) => (
@@ -448,26 +541,38 @@ export default function DeviceDetailPage() {
{account.nickname}
- - {account.status === "normal" ? "正常" : "异常"} + + {account.wechatAliveText}
微信号: {account.wechatId}
-
性别: {account.gender === "male" ? "男" : "女"}
+
性别: {account.gender === 1 ? "男" : "女"}
- 好友数: {account.friendCount} - - {account.addFriendStatus === "enabled" ? "可加友" : "已停用"} + 好友数: {account.totalFriend} + + {account.statusText}
+
最后活跃: {account.lastActive}
))}
) : ( -
-

此设备暂无关联的微信账号

-
+ !accountsLoading && ( +
+

此设备暂无关联的微信账号

+ +
+ ) )}
diff --git a/Cunkebao/app/devices/page.tsx b/Cunkebao/app/devices/page.tsx index 447ca72c..037b4dba 100644 --- a/Cunkebao/app/devices/page.tsx +++ b/Cunkebao/app/devices/page.tsx @@ -5,14 +5,16 @@ import { useRouter } from "next/navigation" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { ChevronLeft, Plus, Filter, Search, RefreshCw, QrCode } from "lucide-react" +import { ChevronLeft, Plus, Filter, Search, RefreshCw, QrCode, Smartphone, Loader2, AlertTriangle } from "lucide-react" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Checkbox } from "@/components/ui/checkbox" import { toast } from "@/components/ui/use-toast" import { Badge } from "@/components/ui/badge" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { fetchDeviceList, deleteDevice } from "@/api/devices" import { ServerDevice } from "@/types/device" +import { api } from "@/lib/api" // 设备接口更新为与服务端接口对应的类型 interface Device extends ServerDevice { @@ -37,6 +39,13 @@ export default function DevicesPage() { const observerTarget = useRef(null) // 使用ref来追踪当前页码,避免依赖effect循环 const pageRef = useRef(1) + // 添加设备相关状态 + const [deviceImei, setDeviceImei] = useState("") + const [deviceName, setDeviceName] = useState("") + const [qrCodeImage, setQrCodeImage] = useState("") + const [isLoadingQRCode, setIsLoadingQRCode] = useState(false) + const [isSubmittingImei, setIsSubmittingImei] = useState(false) + const [activeTab, setActiveTab] = useState("scan") const devicesPerPage = 20 // 每页显示20条记录 @@ -155,6 +164,259 @@ export default function DevicesPage() { } }, [hasMore, isLoading, loadNextPage]) + // 获取设备二维码 + const fetchDeviceQRCode = async () => { + try { + setIsLoadingQRCode(true) + setQrCodeImage("") // 清空当前二维码 + + console.log("正在请求二维码..."); + + // 发起请求获取二维码 - 直接使用fetch避免api工具添加基础URL + const response = await fetch('http://yi.54word.com/v1/api/device/add', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify({}) + }) + + console.log("二维码请求响应状态:", response.status); + + // 保存原始响应文本以便调试 + const responseText = await response.text(); + console.log("原始响应内容:", responseText); + + // 尝试将响应解析为JSON + let result; + try { + result = JSON.parse(responseText); + } catch (e) { + console.error("响应不是有效的JSON:", e); + toast({ + title: "获取二维码失败", + description: "服务器返回的数据格式无效", + variant: "destructive", + }); + return; + } + + console.log("二维码响应数据:", result); + + if (result && result.code === 200) { + // 尝试多种可能的返回数据结构 + let qrcodeData = null; + + if (result.data?.qrCode) { + qrcodeData = result.data.qrCode; + console.log("找到二维码数据在 result.data.qrCode"); + } else if (result.data?.qrcode) { + qrcodeData = result.data.qrcode; + console.log("找到二维码数据在 result.data.qrcode"); + } else if (result.data?.image) { + qrcodeData = result.data.image; + console.log("找到二维码数据在 result.data.image"); + } else if (result.data?.url) { + // 如果返回的是URL而不是base64 + qrcodeData = result.data.url; + console.log("找到二维码URL在 result.data.url"); + setQrCodeImage(qrcodeData); + + toast({ + title: "二维码已更新", + description: "请使用手机扫描新的二维码添加设备", + }); + + return; // 直接返回,不进行base64处理 + } else if (typeof result.data === 'string') { + // 如果data直接是字符串 + qrcodeData = result.data; + console.log("二维码数据直接在 result.data 字符串中"); + } else { + console.error("无法找到二维码数据:", result); + toast({ + title: "获取二维码失败", + description: "返回数据格式不正确", + variant: "destructive", + }); + return; + } + + // 检查数据是否为空 + if (!qrcodeData) { + console.error("二维码数据为空"); + toast({ + title: "获取二维码失败", + description: "服务器返回的二维码数据为空", + variant: "destructive", + }); + return; + } + + console.log("处理前的二维码数据:", qrcodeData); + + // 检查是否已经是完整的data URL + if (qrcodeData.startsWith('data:image')) { + console.log("数据已包含data:image前缀"); + setQrCodeImage(qrcodeData); + } + // 检查是否是URL + else if (qrcodeData.startsWith('http')) { + console.log("数据是HTTP URL"); + setQrCodeImage(qrcodeData); + } + // 尝试作为base64处理 + else { + try { + // 确保base64字符串没有空格等干扰字符 + const cleanedBase64 = qrcodeData.trim(); + console.log("处理后的base64数据:", cleanedBase64.substring(0, 30) + "..."); + + // 直接以图片src格式设置 + setQrCodeImage(`data:image/png;base64,${cleanedBase64}`); + + // 预加载图片,确认是否有效 + const img = new Image(); + img.onload = () => { + console.log("二维码图片加载成功"); + }; + img.onerror = (e) => { + console.error("二维码图片加载失败:", e); + toast({ + title: "二维码加载失败", + description: "服务器返回的数据无法显示为图片", + variant: "destructive", + }); + }; + img.src = `data:image/png;base64,${cleanedBase64}`; + } catch (e) { + console.error("处理base64数据出错:", e); + toast({ + title: "获取二维码失败", + description: "图片数据处理失败", + variant: "destructive", + }); + return; + } + } + + toast({ + title: "二维码已更新", + description: "请使用手机扫描新的二维码添加设备", + }); + } else { + console.error("获取二维码失败:", result); + toast({ + title: "获取二维码失败", + description: result?.msg || "请稍后重试", + variant: "destructive", + }); + } + } catch (error) { + console.error("获取二维码失败", error); + toast({ + title: "获取二维码失败", + description: "请检查网络连接后重试", + variant: "destructive", + }); + } finally { + setIsLoadingQRCode(false); + } + } + + // 打开添加设备模态框时获取二维码 + const handleOpenAddDeviceModal = () => { + setIsAddDeviceOpen(true) + setDeviceImei("") + setDeviceName("") + fetchDeviceQRCode() + } + + // 通过IMEI添加设备 + const handleAddDeviceByImei = async () => { + if (!deviceImei) { + toast({ + title: "IMEI不能为空", + description: "请输入有效的设备IMEI", + variant: "destructive", + }); + return; + } + + try { + setIsSubmittingImei(true); + console.log("正在添加设备,IMEI:", deviceImei, "设备名称:", deviceName); + + // 使用api.post发送请求到/v1/devices + const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/devices`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + body: JSON.stringify({ + imei: deviceImei, + memo: deviceName + }) + }); + + console.log("添加设备响应状态:", response.status); + + // 保存原始响应文本以便调试 + const responseText = await response.text(); + console.log("原始响应内容:", responseText); + + // 尝试将响应解析为JSON + let result; + try { + result = JSON.parse(responseText); + } catch (e) { + console.error("响应不是有效的JSON:", e); + toast({ + title: "添加设备失败", + description: "服务器返回的数据格式无效", + variant: "destructive", + }); + return; + } + + console.log("添加设备响应:", result); + + if (result && result.code === 200) { + toast({ + title: "设备添加成功", + description: result.data?.msg || "设备已成功添加", + }); + + // 清空输入并关闭弹窗 + setDeviceImei(""); + setDeviceName(""); + setIsAddDeviceOpen(false); + + // 刷新设备列表 + loadDevices(1, true); + } else { + console.error("添加设备失败:", result); + toast({ + title: "添加设备失败", + description: result?.msg || "请检查设备信息是否正确", + variant: "destructive", + }); + } + } catch (error) { + console.error("添加设备请求失败:", error); + toast({ + title: "请求失败", + description: "网络错误,请稍后重试", + variant: "destructive", + }); + } finally { + setIsSubmittingImei(false); + } + } + // 刷新设备列表 const handleRefresh = () => { setCurrentPage(1) @@ -231,7 +493,7 @@ export default function DevicesPage() {

设备管理

- @@ -359,22 +621,128 @@ export default function DevicesPage() { 添加设备 -
-
- - -
-
- - -
-
- - -
-
+ + + + + + 扫码添加 + + + + 手动添加 + + + + +
+
+ {isLoadingQRCode ? ( +
+ +

正在获取二维码...

+
+ ) : qrCodeImage ? ( +
+
+ 设备添加二维码 { + console.error("二维码图片加载失败"); + // 隐藏图片 + e.currentTarget.style.display = 'none'; + // 显示错误信息 + const container = document.getElementById('qrcode-container'); + if (container) { + const errorEl = container.querySelector('.qrcode-error'); + if (errorEl) { + errorEl.classList.remove('hidden'); + } + } + }} + /> +
+ +

未能加载二维码,请点击刷新按钮重试

+
+
+

+ 请使用手机扫描此二维码添加设备 +

+
+ ) : ( +
+ +

点击下方按钮获取二维码

+
+ )} +
+ +
+
+ + +
+
+ + setDeviceName(e.target.value)} + /> +

+ 为设备添加一个便于识别的名称 +

+
+
+ + setDeviceImei(e.target.value)} + /> +

+ 请输入设备IMEI码,可在设备信息中查看 +

+
+
+ + +
+
+
+
diff --git a/Server/application/devices/config/route.php b/Server/application/devices/config/route.php index 5c92d024..1bb7b88f 100644 --- a/Server/application/devices/config/route.php +++ b/Server/application/devices/config/route.php @@ -10,6 +10,7 @@ Route::group('v1/', function () { // 设备管理相关 Route::group('devices', function () { + Route::get(':id/related-accounts', 'app\\devices\\controller\\Device@getRelatedAccounts'); // 设备关联微信账号路由 Route::get('', 'app\\devices\\controller\\Device@index'); // 获取设备列表 Route::get('count', 'app\\devices\\controller\\Device@count'); // 获取设备总数 Route::get(':id', 'app\\devices\\controller\\Device@read'); // 获取设备详情 @@ -28,4 +29,6 @@ Route::group('v1/', function () { Route::put('refresh', 'app\\devices\\controller\\DeviceWechat@refresh'); // 刷新设备微信状态 Route::post('transfer-friends', 'app\\devices\\controller\\DeviceWechat@transferFriends'); // 微信好友转移 }); + + })->middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/devices/controller/Device.php b/Server/application/devices/controller/Device.php index 24595a65..4515a492 100644 --- a/Server/application/devices/controller/Device.php +++ b/Server/application/devices/controller/Device.php @@ -181,12 +181,6 @@ class Device extends Controller try { // 获取登录用户信息 $userInfo = request()->userInfo; - if (empty($userInfo)) { - return json([ - 'code' => 401, - 'msg' => '未登录或登录已过期' - ]); - } // 获取设备ID $id = Request::param('id/d'); @@ -277,13 +271,7 @@ class Device extends Controller try { // 获取登录用户信息 $userInfo = request()->userInfo; - if (empty($userInfo)) { - return json([ - 'code' => 401, - 'msg' => '未登录或登录已过期' - ]); - } - + // 检查用户权限,只有管理员可以添加设备 if ($userInfo['isAdmin'] != 1) { return json([ @@ -305,6 +293,7 @@ class Device extends Controller // 验证IMEI是否已存在 $exists = DeviceModel::where('imei', $data['imei'])->where('isDeleted', 0)->find(); + if ($exists) { return json([ 'code' => 400, @@ -314,10 +303,12 @@ class Device extends Controller // 设置设备公司ID $data['companyId'] = $userInfo['companyId']; - + $data['id'] = time(); + // 添加设备 $id = DeviceModel::addDevice($data); - + + // 此处调用底层API return json([ 'code' => 200, 'msg' => '添加成功', @@ -465,4 +456,72 @@ class Device extends Controller return json(['code' => 500, 'msg' => '更新任务配置失败']); } } + + /** + * 获取设备关联的微信账号 + * @return \think\response\Json + */ + public function getRelatedAccounts() + { + try { + // 获取登录用户信息 + $userInfo = request()->userInfo; + + // 获取设备ID + $deviceId = $this->request->param('id/d'); + if (empty($deviceId)) { + return json([ + 'code' => 400, + 'msg' => '设备ID不能为空' + ]); + } + + // 检查用户是否有权限访问该设备 + if ($userInfo['isAdmin'] != 1) { + // 非管理员需要检查是否有权限访问该设备 + $hasPermission = \app\common\model\DeviceUser::checkUserDevicePermission( + $userInfo['id'], + $deviceId, + $userInfo['companyId'] + ); + + if (!$hasPermission) { + return json([ + 'code' => 403, + 'msg' => '您没有权限查看该设备' + ]); + } + } + + // 获取设备信息,确认设备存在 + $device = DeviceModel::where('id', $deviceId) + ->where('isDeleted', 0) + ->find(); + + if (!$device) { + return json([ + 'code' => 404, + 'msg' => '设备不存在或已删除' + ]); + } + + // 获取设备关联的微信账号 + $wechatAccounts = \app\devices\model\DeviceWechatLogin::getDeviceRelatedAccounts($deviceId, $userInfo['companyId']); + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'deviceId' => $deviceId, + 'accounts' => $wechatAccounts, + 'total' => count($wechatAccounts) + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } } \ No newline at end of file diff --git a/Server/application/devices/model/DeviceWechatLogin.php b/Server/application/devices/model/DeviceWechatLogin.php new file mode 100644 index 00000000..138d5cd0 --- /dev/null +++ b/Server/application/devices/model/DeviceWechatLogin.php @@ -0,0 +1,207 @@ + 'integer', + 'deviceId' => 'integer', + 'companyId' => 'integer', + 'createTime' => 'integer' + ]; + + /** + * 查询设备关联的微信ID列表 + * @param int $deviceId 设备ID + * @param int $companyId 公司/租户ID + * @return array 微信ID列表 + */ + public static function getDeviceWechatIds($deviceId, $companyId = null) + { + $query = self::where('deviceId', $deviceId); + + // 如果提供了公司ID,则添加对应的条件 + if ($companyId !== null) { + $query->where('companyId', $companyId); + } + + // 提取微信ID + $records = $query->select(); + $wechatIds = []; + + foreach ($records as $record) { + if (!empty($record['wechatId'])) { + $wechatIds[] = $record['wechatId']; + } + } + + return $wechatIds; + } + + /** + * 根据微信ID查询关联的设备 + * @param string $wechatId 微信ID + * @param int $companyId 公司/租户ID + * @return array 设备ID列表 + */ + public static function getWechatDeviceIds($wechatId, $companyId = null) + { + $query = self::where('wechatId', $wechatId); + + // 如果提供了公司ID,则添加对应的条件 + if ($companyId !== null) { + $query->where('companyId', $companyId); + } + + // 提取设备ID + $records = $query->select(); + $deviceIds = []; + + foreach ($records as $record) { + if (!empty($record['deviceId'])) { + $deviceIds[] = $record['deviceId']; + } + } + + return $deviceIds; + } + + /** + * 添加设备微信登录记录 + * @param int $deviceId 设备ID + * @param string $wechatId 微信ID + * @param int $companyId 公司/租户ID + * @return int 新增记录ID + */ + public static function addRecord($deviceId, $wechatId, $companyId) + { + // 检查是否已存在相同记录 + $exists = self::where('deviceId', $deviceId) + ->where('wechatId', $wechatId) + ->where('companyId', $companyId) + ->find(); + + if ($exists) { + return $exists['id']; + } + + // 创建新记录 + $model = new self(); + $model->deviceId = $deviceId; + $model->wechatId = $wechatId; + $model->companyId = $companyId; + $model->save(); + + return $model->id; + } + + /** + * 删除设备微信登录记录 + * @param int $deviceId 设备ID + * @param string $wechatId 微信ID,为null时删除设备所有记录 + * @param int $companyId 公司/租户ID,为null时不限公司 + * @return bool 删除结果 + */ + public static function removeRecord($deviceId, $wechatId = null, $companyId = null) + { + $query = self::where('deviceId', $deviceId); + + if ($wechatId !== null) { + $query->where('wechatId', $wechatId); + } + + if ($companyId !== null) { + $query->where('companyId', $companyId); + } + + return $query->delete(); + } + + /** + * 关联Device模型 + * @return \think\model\relation\BelongsTo + */ + public function device() + { + return $this->belongsTo('Device', 'deviceId'); + } + + /** + * 获取设备关联的微信账号信息 + * @param int $deviceId 设备ID + * @param int $companyId 公司/租户ID + * @return array 微信账号信息列表 + */ + public static function getDeviceRelatedAccounts($deviceId, $companyId = null) + { + // 获取设备关联的微信ID列表 + $wechatIds = self::getDeviceWechatIds($deviceId, $companyId); + if (empty($wechatIds)) { + return []; + } + + // 查询微信账号信息 + $accounts = \think\Db::name('wechat_account') + ->alias('wa') + ->field([ + 'wa.id', + 'wa.wechatId', + 'wa.accountNickname', + 'wa.nickname', + 'wa.accountUserName', + 'wa.avatar', + 'wa.gender', + 'wa.wechatAlive', + 'wa.status', + 'wa.totalFriend', + 'wa.createTime', + 'wa.updateTime' + ]) + ->whereIn('wa.wechatId', $wechatIds) + ->where('wa.isDeleted', 0) + ->select(); + + // 处理结果数据 + $result = []; + foreach ($accounts as $account) { + // 计算最后活跃时间 + $lastActive = date('Y-m-d H:i:s', max($account['updateTime'], $account['createTime'])); + + // 格式化数据 + $result[] = [ + 'id' => $account['id'], + 'wechatId' => $account['wechatId'], + 'nickname' => $account['accountNickname'] ?: $account['nickname'] ?: '未命名微信', + 'accountUserName' => $account['accountUserName'], + 'avatar' => $account['avatar'], + 'gender' => intval($account['gender']), + 'status' => intval($account['status']), + 'statusText' => intval($account['status']) === 1 ? '可加友' : '已停用', + 'wechatAlive' => intval($account['wechatAlive']), + 'wechatAliveText' => intval($account['wechatAlive']) === 1 ? '正常' : '异常', + 'totalFriend' => intval($account['totalFriend']), + 'lastActive' => $lastActive + ]; + } + + return $result; + } +} \ No newline at end of file