diff --git a/LogBook.md b/LogBook.md index 853804d..3f376c3 100644 --- a/LogBook.md +++ b/LogBook.md @@ -1,3 +1,33 @@ +## 2024-08-08 (MQTT通信日志功能 - 开发完成) + +- **概述**: MQTT通信日志功能模块开发完成。该模块旨在记录系统与MQTT代理之间的所有通信消息,并提供前端界面供管理员查询和审计。 + +- **后端**: + - 实现了独立的MQTT客户端用于监听所有相关主题的上行消息。 + - 通过AOP切面记录了业务系统主动发送的下行消息。 + - 消息数据异步存入 `mqtt_communication_log` 表,确保不阻塞主业务线程。 + - 提供了分页查询API (`/api/admin/mqtt-log/list/page`),支持多种条件筛选和排序。 + - 关键配置项(如日志开关、订阅主题、客户端ID前缀、异步线程池参数)均可通过 `application.yml` 进行配置。 + - 修复了开发过程中的Bean注入冲突和API路径错误。 + +- **前端 (`charging_web_app`)**: + - 创建了MQTT日志查看页面 (`/admin/mqtt-logs`)。 + - 实现了基于Ant Design的查询表单和数据表格,支持分页、条件查询和排序。 + - 表格中的关键字段(如方向、Payload格式、是否保留)已进行本地化中文显示。 + - 解决了API调用404错误和数据无法在表格中正确显示的问题。 + - 在管理员导航栏中添加了页面入口。 + +- **核心功能确认**: `payload` 字段记录的是MQTT消息队列中的原始发送/接收内容。 + +- **当前状态**: 功能已可基本使用。后端日志记录和API服务正常,前端页面可展示和查询日志数据。 + +- **后续待办**: + - 处理前端控制台中剩余的Ant Design Modal `destroyOnClose` 废弃警告 (位于 `DashboardPage`)。 + - 调研并处理Ant Design v5与React 19的兼容性警告。 + - 进行更全面的功能测试,包括各种边界条件和长时间运行的稳定性。 + - (可选) 进一步优化Payload的展示,例如对JSON格式的Payload进行美化或提供折叠/展开功能。 + - (可选) 根据实际需求,考虑日志数据的定期归档或清理策略的实现。 + ## 2024-07-29 (用户激活码功能 - 后端主体) - **核心业务逻辑实现 (Service & Impl)**: @@ -30,4 +60,182 @@ - 编写 API 文档 (Swagger/OpenAPI)。 - 编写单元测试和集成测试。 - 前端页面对接和开发。 - - 根据实际测试反馈进一步完善错误处理和日志。 \ No newline at end of file + - 根据实际测试反馈进一步完善错误处理和日志。 + +## 2024-08-05 (MQTT通信日志系统 - 开发方案) + +- **开发方案文档创建**: + - 在 `springboot-init-main/doc/` 目录下创建了 `mqtt_communication_log_plan.md` 文件,详细描述了MQTT通信日志系统的设计与实现方案。 + - 该系统旨在完整记录单片机与服务器之间的所有MQTT消息通信,用于通信可视化、故障诊断、性能分析和安全审计。 + +- **系统定位与设计原则**: + - 纯日志记录系统,不干扰正常业务流程,不消费消息队列中的数据 + - 采用"监听者模式",通过独立的MQTT客户端进行旁路监听 + - 异步记录消息,确保不影响主业务性能 + +- **核心架构设计**: + - 利用已有的 `mqtt_communication_log` 数据表存储日志数据 + - 设计了 `MqttCommunicationLogger`、`MqttLogService` 和 `MqttLogController` 三个核心组件 + - 使用AOP技术拦截发送消息,记录下行消息内容 + - 通过独立MQTT客户端订阅同样的主题,记录上行消息内容 + +- **技术实现细节**: + - 设计了消息监听、记录、解析和关联的具体实现方法 + - 提供了完整的配置项和部署说明 + - 包含数据库维护策略,避免日志数据膨胀 + +- **风险评估与解决方案**: + - 识别了性能影响、存储膨胀、消息完整性等技术风险 + - 提出了敏感信息处理和数据一致性等业务风险的解决方案 + +- **项目计划**: + - 制定了包括设计、开发、测试、部署在内的完整计划 + - 总体开发周期预计为11个工作日 + +- **下一步**: + - 开始实施开发方案,首先进行后端核心功能实现 + - 准备测试数据和测试用例 + - 开发管理员日志查询界面 + +## 2024-08-06 (MQTT通信日志系统 - 后端核心功能实现) + +- **配置与属性类**: + - 在 `application.yml` 中添加了 `mqtt.logger` 相关配置,包括启用开关、客户端ID前缀、订阅主题、异步线程池参数和日志保留策略。 + - 创建了 `MqttLoggerProperties.java` 用于映射这些配置。 + +- **数据模型与Mapper**: + - 创建了 `MqttCommunicationLog.java` 实体类,对应数据库表 `mqtt_communication_log`。 + - 创建了 `MqttCommunicationLogMapper.java` MyBatis Plus接口。 + +- **核心服务层**: + - 创建了 `MqttCommunicationLogService.java` 接口,定义了异步记录上行和下行消息的方法。 + - 创建了 `MqttCommunicationLogServiceImpl.java` 实现类,包含: + - `asyncLogMessage`: 异步记录完整的MQTT消息,包括方向、客户端ID、主题、Payload、QoS等,并进行初步的Payload格式判断(TEXT/JSON)。 + - `asyncLogDownstreamMessage`: 异步记录出站消息的简化版本。 + - 使用 `@Async("mqttLoggerThreadPoolTaskExecutor")` 指定自定义线程池执行异步任务。 + +- **异步配置**: + - 创建了 `MqttLoggerAsyncConfigurer.java`,配置了名为 `mqttLoggerThreadPoolTaskExecutor` 的专用线程池,用于异步写入日志,避免阻塞主线程。 + +- **MQTT日志专用客户端**: + - 创建了 `MqttLoggerClientConfig.java`,用于配置和创建专用于日志记录的MQTT客户端 (`mqttLoggerClient`)。此客户端懒加载,并使用与主业务不同的客户端ID。 + - 创建了 `MqttLoggerCallbackHandler.java`,作为日志客户端的回调处理器: + - 在 `connectComplete` 中,根据配置订阅 `mqtt.logger.topics` 中指定的主题,用于监听上行消息。 + - 在 `messageArrived` 中,调用 `logService.asyncLogMessage` 记录接收到的上行消息。 + - 创建了 `MqttLoggerConnectionManager.java`,负责在应用启动时连接日志客户端,并在应用关闭时断开和关闭客户端。 + +- **出站消息记录 (AOP)**: + - 创建了 `MqttPublishLogAspect.java` 切面,通过 `@Around` 注解拦截 `MqttServiceImpl.sendCommand` 方法。 + - 在目标方法执行后,调用 `logService.asyncLogDownstreamMessage` 记录出站(DOWNSTREAM)消息的详情。 + - 为了能关联到具体的业务任务,在 `RobotTaskService` 接口及其实现类 `RobotTaskServiceImpl` 中添加了 `findLatestTaskByRobotIdAndSessionId` 方法,以便AOP切面可以根据 `robotId` 和 `sessionId` 查询关联的 `taskId`。 + +- **管理API接口**: + - 创建了 `MqttCommunicationLogController.java`,提供了 `/api/admin/mqtt-log/list/page` 接口,允许管理员分页查询MQTT通信日志。 + - 创建了 `MqttLogQueryRequest.java` DTO,用于封装查询条件,包括消息ID、方向、客户端ID、主题、Payload内容、QoS、时间范围、关联会话ID和任务ID等。 + - 控制器方法使用 `@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)` 进行了权限控制。 + +- **问题修复**: + - 修复了 `MqttLoggerConnectionManager.java` 中 `disconnectFromMqtt` 方法内误用 `mqttLoggerClient.isOpen()`(此方法不存在)导致的编译错误。修改为直接调用 `mqttLoggerClient.close()`。 + - 解决了 `MqttPublishLogAspect` 中因存在两个 `MqttClient` 类型的Bean (`mqttClientBean` 和 `mqttLoggerClient`) 而导致的 `NoUniqueBeanDefinitionException` 启动错误。通过在 `MqttPublishLogAspect` 的构造函数中为 `MqttClient` 参数添加 `@Qualifier("mqttClientBean")` 注解,明确指定注入主业务MQTT客户端。 + - 解决了 `MqttConnectionManager` 中类似的 `NoUniqueBeanDefinitionException` 启动错误。通过在其构造函数中为 `MqttClient` 参数添加 `@Qualifier("mqttClientBean")` 注解,明确指定注入主业务MQTT客户端。 + +- **状态**: 后端核心功能已开发完成,具备了独立的MQTT客户端监听上行消息和通过AOP记录下行消息的能力,并将日志异步存入数据库。同时提供了管理员查询日志的API接口。相关启动错误已修复。 + +- **下一步**: + - 前端日志查看与管理界面的开发。 + - 对日志记录的准确性和完整性进行全面测试。 + - 根据测试结果进行优化和调整。 + +## 2024-08-07 (MQTT通信日志系统 - 前端页面与导航) + +- **页面创建与组件**: + - 在 `charging_web_app/src/app/admin/mqtt-logs/` 目录下创建了 `page.tsx`,作为MQTT通信日志的查看页面。 + - 使用 Ant Design 组件 (`Card`, `Form`, `Input`, `Select`, `DatePicker`, `Button`, `Table`, `Tag`, `Tooltip`, `message`) 构建了页面结构。 + - 包含一个查询表单,其字段对应后端的 `MqttLogQueryRequest`。 + - 包含一个数据表格,列定义对应后端的 `MqttCommunicationLog` 实体,并对部分字段(如日期、方向、Payload)进行了格式化显示。 + - 实现了基本的查询、重置、分页和客户端排序逻辑的骨架。 + +- **类型定义**: + - 创建了 `charging_web_app/src/types/log.types.ts` 文件,定义了前端所需的 `MqttCommunicationLog`、`MqttLogQueryRequest` 和通用分页响应 `PageResponse` 接口。 + +- **API服务**: + - 创建了 `charging_web_app/src/services/axiosInstance.ts`,提供了一个基础的全局 Axios 实例配置。 + - 创建了 `charging_web_app/src/services/logService.ts`,其中包含 `fetchMqttLogs`异步函数,用于调用后端 `/api/admin/mqtt-log/list/page` 接口获取日志数据。 + - 解决了 `logService.ts` 中因 `axiosInstance` 相对路径导入问题导致的TypeScript编译错误,改为使用 `@/services/axiosInstance` 别名路径。 + +- **页面逻辑集成**: + - `mqtt-logs/page.tsx` 导入了类型定义和API服务函数。 + - 实现了 `useEffect` Hook 在页面加载时获取初始日志数据。 + - 完善了表单提交 (`handleSearch`)、表单重置 (`handleReset`) 和表格变化 (`handleTableChange`) 的处理函数,使其能够正确调用API获取和展示数据。 + - 使用 Ant Design `message` 组件在API请求失败时给出用户提示。 + +- **导航链接添加**: + - 在 `charging_web_app/src/app/(authenticated)/layout.tsx` 文件中,为管理员角色的用户在头部导航栏添加了一个"MQTT日志"链接,指向 `/admin/mqtt-logs` 页面。 + - 使用了 Next.js 的 `Link` 组件以优化导航体验。 + +- **状态**: MQTT通信日志查看页面的前端基本框架已完成,包括UI界面、API对接和导航入口。可以进行初步的功能测试。 + +- **下一步**: + - 全面测试前端页面的查询、分页、排序、重置等功能。 + - 确保与后端API的交互无误,数据能正确展示和更新。 + - 根据测试反馈和视觉需求,进一步优化页面样式和用户交互体验。 + - 考虑在Payload列增加更完善的JSON格式化显示或代码高亮功能(如果需要)。 + - 检查并确保所有代码符合项目规范和最佳实践。 + +## 2024-08-08 (MQTT通信日志系统 - 前端表格列本地化) + +- **需求**: 将MQTT日志表格中的 `direction`, `payloadFormat`, `isRetained` 等字段值转换为中文显示,提升可读性。 +- **解决方案**: + - 修改 `charging_web_app/src/app/admin/mqtt-logs/page.tsx` 文件中 `columns` 的定义。 + - 为 `direction` 列的 `render` 函数增加逻辑:`UPSTREAM` -> `上行` (蓝色标签), `DOWNSTREAM` -> `下行` (绿色标签)。 + - 为 `payloadFormat` 列的 `render` 函数增加逻辑:`TEXT` -> `文本`, `JSON` -> `JSON`。 + - `isRetained` 列的 `render` 函数已能正确处理布尔值到 `是`/`否` 的转换。 +- **状态**: 表格相关列的显示已本地化。 +- **下一步**: + - 用户验证本地化显示效果。 + - 处理Ant Design Modal `destroyOnClose` 废弃警告和 React 19 兼容性警告。 + +## 2024-08-08 (MQTT通信日志系统 - 前端数据显示修复) + +- **问题定位**: + - 前端API调用成功并获取到数据,但Ant Design Table显示"No data"。 + - 原因在于前端从API响应中提取数据的方式与实际的 `BaseResponse>` 嵌套结构不匹配。 + - `fetchMqttLogs` 函数返回的类型与其实际获取的数据结构存在偏差,导致在页面组件中访问 `response.data` 时类型不匹配,进而无法正确提取 `records` 和 `total`。 + +- **解决方案**: + 1. **修改 `charging_web_app/src/services/logService.ts`**: + - 添加了 `BaseResponse` 接口定义以匹配后端通用响应结构。 + - 更新了 `fetchMqttLogs` 函数的返回类型为 `Promise>>`。 + - 更新了 `axiosInstance.post` 的泛型参数为 `BaseResponse>`。 + - 确保 `fetchMqttLogs` 返回的是从Axios响应中获取的 `response.data`,这个 `data` 实际上是后端的 `BaseResponse` 对象。 + 2. **修改 `charging_web_app/src/app/admin/mqtt-logs/page.tsx`**: + - 在 `loadLogs` 函数中,当接收到 `fetchMqttLogs` 的结果 (`response`) 时,数据提取逻辑调整为从 `response.data` (即 `PageResponse` 对象) 中获取 `records`、`total` 等分页信息。 + - 例如: `const pageData = response.data; setLogs(pageData.records);` + +- **状态**: 已调整前端服务层和页面组件的数据处理逻辑,以正确解析后端返回的嵌套响应结构。预计数据能够正确显示在表格中。 + +- **下一步**: + - 用户验证数据是否已在前端表格中正确显示。 + - 检查分页、查询、排序等功能是否均正常工作。 + - 继续处理前端控制台剩余的警告。 + +## 2024-08-08 (MQTT通信日志系统 - 404错误修复) + +- **问题定位**: + - 前端请求MQTT日志列表API (`/api/admin/mqtt-log/list/page`) 时出现 404 错误。 + - 后端 `application.yml` 中配置了 `server.servlet.context-path: /api`。 + - 后端 `MqttCommunicationLogController` 的 `@RequestMapping` 错写为 `"/api/admin/mqtt-log"`。 + - 导致实际的API路径变为 `/api` (context-path) + `/api/admin/mqtt-log` (controller mapping) = `/api/api/admin/mqtt-log/list/page`。 + - 而前端请求路径为 `/api/admin/mqtt-log/list/page`,因此不匹配。 + +- **解决方案**: + - 修改 `springboot-init-main/src/main/java/com/yupi/project/controller/MqttCommunicationLogController.java`。 + - 将 `@RequestMapping("/api/admin/mqtt-log")` 修改为 `@RequestMapping("/admin/mqtt-log")`。 + - 这样,结合 `context-path`,正确的API路径为 `/api/admin/mqtt-log/list/page`,与前端请求一致。 + +- **状态**: 已修改后端Controller的路径,预计能解决404问题。等待用户重启后端服务并验证。 + +- **下一步**: + - 用户验证404错误是否已解决。 + - 继续处理前端控制台的其他警告(`Link` `legacyBehavior` 已处理,Ant Design Modal `destroyOnClose` 和 React 19 兼容性问题)。 + - 检查并确保所有代码符合项目规范和最佳实践。 \ No newline at end of file diff --git a/charging_web_app/src/app/(authenticated)/layout.tsx b/charging_web_app/src/app/(authenticated)/layout.tsx index 2bbce42..2c0eba4 100644 --- a/charging_web_app/src/app/(authenticated)/layout.tsx +++ b/charging_web_app/src/app/(authenticated)/layout.tsx @@ -3,7 +3,7 @@ import React, { useEffect } from 'react'; import { useAuth } from '@/contexts/AuthContext'; import { useRouter } from 'next/navigation'; -// import Link from 'next/link'; // Temporarily remove Link to test a plain anchor +import Link from 'next/link'; import LoadingSpinner from '@/components/LoadingSpinner'; export default function AuthenticatedLayout({ @@ -11,18 +11,16 @@ export default function AuthenticatedLayout({ }: { children: React.ReactNode; }) { - const { isAuthenticated, isLoading, user, logout } = useAuth(); // 获取 user 和 logout + const { isAuthenticated, isLoading, user, logout } = useAuth(); const router = useRouter(); useEffect(() => { - // Only redirect if loading is complete and user is not authenticated if (!isLoading && !isAuthenticated) { console.log('AuthenticatedLayout: User not authenticated, redirecting to login.'); router.replace('/login'); } }, [isAuthenticated, isLoading, router]); - // If still loading, show spinner if (isLoading) { return (
@@ -31,9 +29,7 @@ export default function AuthenticatedLayout({ ); } - // If loading is complete but user is not authenticated, - // useEffect will handle redirection. Render null or spinner to avoid flashing content. - if (!isAuthenticated || !user) { // Also check if user object is available + if (!isAuthenticated || !user) { return (
@@ -41,16 +37,24 @@ export default function AuthenticatedLayout({ ); } - // If authenticated, render children + const handleLogout = async () => { + try { + await logout(); + router.push('/login'); + } catch (error) { + console.error('注销失败:', error); + } + }; + return (