From db7b3aa7af17c66b19358f99af3400a91c3b0aff 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 15:09:18 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E8=AE=BE=E5=A4=87=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=8A=B6=E6=80=81?= 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 | 93 ++++++++- Server/application/devices/config/route.php | 1 + .../application/devices/controller/Device.php | 184 ++++++++++++++++-- .../devices/model/DeviceHandleLog.php | 121 ++++++++++++ 5 files changed, 382 insertions(+), 22 deletions(-) create mode 100644 Server/application/devices/model/DeviceHandleLog.php diff --git a/Cunkebao/api/devices.ts b/Cunkebao/api/devices.ts index e121be43..8f13f92c 100644 --- a/Cunkebao/api/devices.ts +++ b/Cunkebao/api/devices.ts @@ -38,6 +38,11 @@ export const fetchDeviceRelatedAccounts = async (id: string | number): Promise>(`/v1/devices/${id}/related-accounts`); }; +// 获取设备操作记录 +export const fetchDeviceHandleLogs = async (id: string | number, page: number = 1, limit: number = 10): Promise> => { + return api.get>(`/v1/devices/${id}/handle-logs?page=${page}&limit=${limit}`); +}; + // 更新设备任务配置 export const updateDeviceTaskConfig = async ( id: string | number, diff --git a/Cunkebao/app/devices/[id]/page.tsx b/Cunkebao/app/devices/[id]/page.tsx index 86a629e8..521741eb 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, RefreshCw } from "lucide-react" +import { ChevronLeft, Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History, RefreshCw, Loader2 } 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, fetchDeviceRelatedAccounts, updateDeviceTaskConfig } from "@/api/devices" +import { fetchDeviceDetail, fetchDeviceRelatedAccounts, updateDeviceTaskConfig, fetchDeviceHandleLogs } from "@/api/devices" import { toast } from "sonner" interface WechatAccount { @@ -65,6 +65,14 @@ function getBadgeVariant(status: string): "default" | "destructive" | "outline" } } +// 添加操作记录接口 +interface HandleLog { + id: string | number; + content: string; // 操作说明 + username: string; // 操作人 + createTime: string; // 操作时间 +} + export default function DeviceDetailPage() { const params = useParams() const router = useRouter() @@ -72,6 +80,8 @@ export default function DeviceDetailPage() { const [activeTab, setActiveTab] = useState("info") const [loading, setLoading] = useState(true) const [accountsLoading, setAccountsLoading] = useState(false) + const [logsLoading, setLogsLoading] = useState(false) + const [handleLogs, setHandleLogs] = useState([]) const [savingFeatures, setSavingFeatures] = useState({ autoAddFriend: false, autoReply: false, @@ -284,6 +294,34 @@ export default function DeviceDetailPage() { } } + // 获取设备操作记录 + const fetchHandleLogs = async () => { + if (!params.id || logsLoading) return + + try { + setLogsLoading(true) + const response = await fetchDeviceHandleLogs(params.id as string) + + if (response && response.code === 200 && response.data) { + const logs = response.data.list || [] + setHandleLogs(logs) + + if (logs.length > 0) { + console.log('获取到操作记录:', logs.length) + } else { + console.log('设备暂无操作记录') + } + } else { + toast.error("获取操作记录失败") + } + } catch (error) { + console.error("获取操作记录失败:", error) + toast.error("获取操作记录失败,请稍后重试") + } finally { + setLogsLoading(false) + } + } + // 处理标签页切换 const handleTabChange = (value: string) => { setActiveTab(value) @@ -292,6 +330,11 @@ export default function DeviceDetailPage() { if (value === "accounts") { fetchRelatedAccounts() } + + // 当切换到"操作记录"标签时,获取最新的操作记录 + if (value === "history") { + fetchHandleLogs() + } } // 处理功能开关状态变化 @@ -580,18 +623,45 @@ export default function DeviceDetailPage() { +
+

操作记录

+ +
+ - {device.history && device.history.length > 0 ? ( + {logsLoading && handleLogs.length === 0 ? ( +
+
+ 加载操作记录中... +
+ ) : handleLogs.length > 0 ? (
- {device.history.map((record, index) => ( -
+ {handleLogs.map((log) => ( +
-
{record.action}
+
{log.content}
- 操作人: {record.operator} · {record.time} + 操作人: {log.username} · {log.createTime}
@@ -600,6 +670,15 @@ export default function DeviceDetailPage() { ) : (

暂无操作记录

+
)} diff --git a/Server/application/devices/config/route.php b/Server/application/devices/config/route.php index 1bb7b88f..58190466 100644 --- a/Server/application/devices/config/route.php +++ b/Server/application/devices/config/route.php @@ -11,6 +11,7 @@ Route::group('v1/', function () { // 设备管理相关 Route::group('devices', function () { Route::get(':id/related-accounts', 'app\\devices\\controller\\Device@getRelatedAccounts'); // 设备关联微信账号路由 + Route::get(':id/handle-logs', 'app\\devices\\controller\\Device@handleLogs'); // 获取设备操作记录 Route::get('', 'app\\devices\\controller\\Device@index'); // 获取设备列表 Route::get('count', 'app\\devices\\controller\\Device@count'); // 获取设备总数 Route::get(':id', 'app\\devices\\controller\\Device@read'); // 获取设备详情 diff --git a/Server/application/devices/controller/Device.php b/Server/application/devices/controller/Device.php index 4515a492..2e8168ca 100644 --- a/Server/application/devices/controller/Device.php +++ b/Server/application/devices/controller/Device.php @@ -1,8 +1,10 @@ $data['imei'], + 'userId' => $userInfo['id'], + 'content' => '添加设备', + 'companyId' => $userInfo['companyId'], + ] + ); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + + return json([ + 'code' => 500, + 'msg' => '添加失败:' . $e->getMessage() + ]); + } // 此处调用底层API return json([ @@ -333,12 +357,6 @@ class Device extends Controller try { // 获取登录用户信息 $userInfo = request()->userInfo; - if (empty($userInfo)) { - return json([ - 'code' => 401, - 'msg' => '未登录或登录已过期' - ]); - } // 检查用户权限,只有管理员可以删除设备 if ($userInfo['isAdmin'] != 1) { @@ -396,6 +414,9 @@ class Device extends Controller { // 获取请求参数 $data = $this->request->post(); + + // 获取登录用户信息 + $userInfo = request()->userInfo; // 验证参数 if (empty($data['id'])) { @@ -436,14 +457,53 @@ class Device extends Controller if (!$hasUpdate) { return json(['code' => 200, 'msg' => '更新成功', 'data' => ['taskConfig' => $taskConfig]]); } - - // 更新设备taskConfig字段 - $result = \app\devices\model\Device::where('id', $deviceId) - ->update([ - 'taskConfig' => json_encode($taskConfig), - 'updateTime' => time() - ]); + + try { + Db::startTrans(); + + // 更新设备taskConfig字段 + $result = \app\devices\model\Device::where('id', $deviceId) + ->update([ + 'taskConfig' => json_encode($taskConfig), + 'updateTime' => time() + ]); + + if (isset($data['autoAddFriend'])) { + $content = $data['autoAddFriend'] ? '开启自动添加好友' : '关闭自动添加好友'; + } + + if (isset($data['autoReply'])) { + $content = $data['autoReply'] ? '开启自动回复' : '关闭自动回复'; + } + + if (isset($data['momentsSync'])) { + $content = $data['momentsSync'] ? '开启朋友圈同步' : '关闭朋友圈同步'; + } + + if (isset($data['aiChat'])) { + $content = $data['aiChat'] ? '开启AI会话' : '关闭AI会话'; + } + + // 添加设备操作记录 + DeviceHandleLog::addLog( + [ + 'imei' => $device['imei'], + 'deviceId' => $deviceId, + 'userId' => $userInfo['id'], + 'content' => $content, + 'companyId' => $userInfo['companyId'], + ] + ); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + return json([ + 'code' => 500, + 'msg' => '更新任务配置失败' + ]); + } + if ($result) { return json([ 'code' => 200, @@ -524,4 +584,98 @@ class Device extends Controller ]); } } + + /** + * 获取设备操作记录 + * @return \think\response\Json + */ + public function handleLogs() + { + 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' => '设备不存在或已删除' + ]); + } + + // 获取分页参数 + $page = (int)Request::param('page', 1); + $limit = (int)Request::param('limit', 10); + + // 查询设备操作记录,并关联用户表获取操作人信息 + $logs = Db::table('tk_device_handle_log') + ->alias('l') + ->join('tk_users u', 'l.userId = u.id', 'left') + ->where('l.imei', $device['imei']) + ->where('l.companyId', $userInfo['companyId']) + ->field([ + 'l.id', + 'l.content', + 'l.createTime', + 'u.username' + ]) + ->order('l.createTime desc') + ->paginate($limit, false, ['page' => $page]); + + // 格式化返回数据 + $items = []; + foreach ($logs as $log) { + $items[] = [ + 'id' => $log['id'], + 'content' => $log['content'], + 'username' => $log['username'] ? $log['username'] : '未知用户', + 'createTime' => $log['createTime'] + ]; + } + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'total' => $logs->total(), + 'list' => $items + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } } \ No newline at end of file diff --git a/Server/application/devices/model/DeviceHandleLog.php b/Server/application/devices/model/DeviceHandleLog.php new file mode 100644 index 00000000..70ef7b11 --- /dev/null +++ b/Server/application/devices/model/DeviceHandleLog.php @@ -0,0 +1,121 @@ + 'integer', + 'userId' => 'integer', + 'deviceId' => 'integer', + 'companyId' => 'integer', + 'createTime' => 'datetime' + ]; + + /** + * 添加设备操作日志 + * @param array $data 日志数据 + * @return int 新增日志ID + */ + public static function addLog($data) + { + $log = new self(); + $log->allowField(true)->save($data); + return $log->id; + } + + /** + * 获取设备操作日志列表 + * @param array $where 查询条件 + * @param string $order 排序方式 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator 分页对象 + */ + public static function getLogList($where = [], $order = 'createTime desc', $page = 1, $limit = 10) + { + return self::where($where) + ->order($order) + ->paginate($limit, false, ['page' => $page]); + } + + /** + * 根据IMEI获取设备操作日志 + * @param string $imei 设备IMEI + * @param int $companyId 租户ID + * @param int $limit 获取条数 + * @return array 日志记录 + */ + public static function getLogsByImei($imei, $companyId = null, $limit = 10) + { + $query = self::where('imei', $imei); + + if ($companyId !== null) { + $query->where('companyId', $companyId); + } + + return $query->order('createTime', 'desc') + ->limit($limit) + ->select(); + } + + /** + * 根据用户ID获取操作日志 + * @param int $userId 用户ID + * @param int $companyId 租户ID + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator 分页对象 + */ + public static function getLogsByUser($userId, $companyId = null, $page = 1, $limit = 10) + { + $query = self::where('userId', $userId); + + if ($companyId !== null) { + $query->where('companyId', $companyId); + } + + return $query->order('createTime', 'desc') + ->paginate($limit, false, ['page' => $page]); + } + + /** + * 记录设备操作日志的便捷方法 + * @param string $imei 设备IMEI + * @param int $userId 操作用户ID + * @param string $content 操作内容 + * @param int $companyId 租户ID + * @return int 日志ID + */ + public static function recordLog($imei, $userId, $content, $companyId = null) + { + $data = [ + 'imei' => $imei, + 'userId' => $userId, + 'content' => $content, + 'companyId' => $companyId + ]; + + return self::addLog($data); + } +} \ No newline at end of file