# ReportAdmin **Repository Path**: daao/report-admin ## Basic Information - **Project Name**: ReportAdmin - **Description**: ReportAdmin - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-05 - **Last Updated**: 2026-02-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 前端错误日志上报系统 一个简单的前端错误日志收集和管理系统,用于收集和查看玩家上报的错误日志。 ## ✨ 功能特性 - 📤 **日志上报**:支持玩家通过 API 上传日志文件(multipart/form-data) - 📊 **列表展示**:分页展示日志列表,包含玩家 UID、昵称、时间、文件大小等信息 - 🔍 **筛选搜索**:按玩家 UID、时间范围快速筛选日志 - ⏱️ **排序功能**:支持按时间、玩家 UID 升序/降序排列 - 📄 **详情查看**:查看单条日志的完整内容和元信息 - 🗑️ **删除管理**:支持删除不需要的日志记录 ## 🛠️ 技术栈 - **后端**:Express + TypeScript + Multer - **前端**:React 18 + Ant Design 5 + Vite + TypeScript - **存储**:本地文件系统(JSON 索引 + 文本文件) - **开发工具**:tsx(后端热重载)、ESLint、Prettier ## 项目结构 ``` report-admin/ ├── server/ # 后端服务 │ ├── src/ │ │ ├── index.ts # 入口文件 │ │ ├── routes/logs.ts # API 路由 │ │ ├── services/logService.ts # 日志存储服务 │ │ └── types/index.ts # 类型定义 │ └── logs/ # 日志文件存储目录 │ ├── client/ # 前端应用 │ └── src/ │ ├── App.tsx │ ├── pages/LogList.tsx # 日志列表页 │ ├── pages/LogDetail.tsx # 日志详情页 │ └── api/logs.ts # API 请求 │ └── package.json # 根目录配置 ``` ## 快速开始 ### 环境要求 - Node.js >= 16.0.0 - npm >= 7.0.0 或 yarn >= 1.22.0 ### 安装依赖 ```bash # 安装后端依赖 cd server npm install # 安装前端依赖 cd ../client npm install ``` ### 启动服务 **开发模式(推荐):** ```bash # 终端 1:启动后端服务 (端口 4000) cd server npm run dev # 终端 2:启动前端开发服务器 (端口 5173) cd client npm run dev ``` **生产模式:** ```bash # 构建前端 cd client npm run build # 构建并启动后端 cd ../server npm run build npm start ``` 访问 [http://localhost:5173](http://localhost:5173) 查看管理界面。 ### 环境配置(可选) 在 `server/` 目录下创建 `.env` 文件: ```env PORT=4000 LOGS_DIR=./logs MAX_FILE_SIZE=10485760 # 10MB ``` ## API 接口 ### 1. 上传日志 ``` POST /api/logs/upload Content-Type: multipart/form-data ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | uid | string | 是 | 玩家ID | | nickname | string | 是 | 玩家昵称 | | timestamp | number | 是 | 时间戳 | | file | File | 是 | 日志文件 | **响应示例:** ```json { "success": true, "message": "上传成功", "data": { "id": "log_1707100000000_abc123" } } ``` ### 2. 获取日志列表 ``` GET /api/logs ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | uid | string | 否 | 按玩家ID筛选 | | startTime | number | 否 | 开始时间戳 | | endTime | number | 否 | 结束时间戳 | | sortBy | string | 否 | 排序字段:time / uid | | sortOrder | string | 否 | 排序方向:asc / desc | | page | number | 否 | 页码,默认 1 | | pageSize | number | 否 | 每页条数,默认 20 | **响应示例:** ```json { "success": true, "data": [...], "total": 100, "page": 1, "pageSize": 20 } ``` ### 3. 获取日志详情 ``` GET /api/logs/:id ``` **响应示例:** ```json { "success": true, "data": { "id": "log_1707100000000_abc123", "uid": "player_001", "nickname": "玩家昵称", "timestamp": 1707100000000, "filename": "log_1707100000000_abc123.txt", "size": 1024, "createdAt": 1707100000000, "content": "日志内容..." } } ``` ### 4. 删除日志 ``` DELETE /api/logs/:id ``` **响应示例:** ```json { "success": true, "message": "删除成功" } ``` ## 客户端上传示例 ### Unity Lua(推荐) ```lua -- 使用 UnityWebRequest 上传日志 local function UploadErrorLog(uid, nickname, logContent) local CS = CS local UnityWebRequest = CS.UnityEngine.Networking.UnityWebRequest local UploadHandlerRaw = CS.UnityEngine.Networking.UploadHandlerRaw local DownloadHandlerBuffer = CS.UnityEngine.Networking.DownloadHandlerBuffer -- 构建 multipart/form-data local boundary = "----WebKitFormBoundary" .. os.time() local bodyData = "" -- 添加 uid 字段 bodyData = bodyData .. "--" .. boundary .. "\r\n" bodyData = bodyData .. 'Content-Disposition: form-data; name="uid"\r\n\r\n' bodyData = bodyData .. uid .. "\r\n" -- 添加 nickname 字段 bodyData = bodyData .. "--" .. boundary .. "\r\n" bodyData = bodyData .. 'Content-Disposition: form-data; name="nickname"\r\n\r\n' bodyData = bodyData .. nickname .. "\r\n" -- 添加 timestamp 字段 bodyData = bodyData .. "--" .. boundary .. "\r\n" bodyData = bodyData .. 'Content-Disposition: form-data; name="timestamp"\r\n\r\n' bodyData = bodyData .. tostring(os.time() * 1000) .. "\r\n" -- 添加 file 字段 bodyData = bodyData .. "--" .. boundary .. "\r\n" bodyData = bodyData .. 'Content-Disposition: form-data; name="file"; filename="error.log"\r\n' bodyData = bodyData .. 'Content-Type: text/plain\r\n\r\n' bodyData = bodyData .. logContent .. "\r\n" bodyData = bodyData .. "--" .. boundary .. "--\r\n" -- 转换为字节数组 local bytes = CS.System.Text.Encoding.UTF8:GetBytes(bodyData) -- 创建请求 local url = "http://localhost:4000/api/logs/upload" local request = UnityWebRequest.Post(url, "") request.uploadHandler = UploadHandlerRaw(bytes) request.downloadHandler = DownloadHandlerBuffer() request:SetRequestHeader("Content-Type", "multipart/form-data; boundary=" .. boundary) -- 发送请求 request:SendWebRequest() -- 协程等待结果(可选) -- while not request.isDone do -- coroutine.yield() -- end -- if request.result == CS.UnityEngine.Networking.UnityWebRequest.Result.Success then -- print("日志上传成功: " .. request.downloadHandler.text) -- else -- print("日志上传失败: " .. request.error) -- end return request end -- 使用示例 local playerUid = "player_12345" local playerNickname = "玩家昵称" local errorLog = [[ [ERROR] 2024-02-05 10:30:25 NullReferenceException: Object reference not set to an instance of an object at GameManager.Update() in Assets/Scripts/GameManager.cs:line 42 at UnityEngine.CoreModule.dll:0 ]] UploadErrorLog(playerUid, playerNickname, errorLog) ``` ### Unity Lua(使用 WWWForm 简化版本) ```lua -- 使用 WWWForm 简化上传 local function UploadErrorLogSimple(uid, nickname, logContent) local CS = CS local UnityWebRequest = CS.UnityEngine.Networking.UnityWebRequest local WWWForm = CS.UnityEngine.WWWForm -- 创建表单 local form = WWWForm() form:AddField("uid", uid) form:AddField("nickname", nickname) form:AddField("timestamp", tostring(os.time() * 1000)) -- 添加文件(从字符串创建) local bytes = CS.System.Text.Encoding.UTF8:GetBytes(logContent) form:AddBinaryData("file", bytes, "error.log", "text/plain") -- 创建并发送请求 local url = "http://localhost:4000/api/logs/upload" local request = UnityWebRequest.Post(url, form) request:SendWebRequest() return request end -- 使用示例 UploadErrorLogSimple("player_12345", "玩家昵称", "错误日志内容...") ``` ### Unity Lua(全局错误捕获示例) ```lua -- 自动捕获 Lua 错误并上传 local LogUploader = {} LogUploader.serverUrl = "http://your-server.com:4000/api/logs/upload" LogUploader.playerUid = "" LogUploader.playerNickname = "" function LogUploader.Init(uid, nickname) LogUploader.playerUid = uid LogUploader.playerNickname = nickname -- 设置 Lua 错误处理 local oldErrorHandler = _G.errorHandler _G.errorHandler = function(msg) LogUploader.CaptureError(msg) if oldErrorHandler then oldErrorHandler(msg) end end end function LogUploader.CaptureError(errorMsg) local timestamp = os.date("%Y-%m-%d %H:%M:%S") local stackTrace = debug.traceback("", 2) local logContent = string.format([[ [Lua Error] %s Player: %s (%s) Error: %s Stack Trace: %s ]], timestamp, LogUploader.playerNickname, LogUploader.playerUid, errorMsg, stackTrace) LogUploader.Upload(logContent) end function LogUploader.Upload(logContent) local CS = CS local WWWForm = CS.UnityEngine.WWWForm local UnityWebRequest = CS.UnityEngine.Networking.UnityWebRequest local form = WWWForm() form:AddField("uid", LogUploader.playerUid) form:AddField("nickname", LogUploader.playerNickname) form:AddField("timestamp", tostring(os.time() * 1000)) local bytes = CS.System.Text.Encoding.UTF8:GetBytes(logContent) form:AddBinaryData("file", bytes, "error.log", "text/plain") local request = UnityWebRequest.Post(LogUploader.serverUrl, form) request:SendWebRequest() end -- 初始化(游戏启动时调用) LogUploader.Init("player_12345", "测试玩家") ``` ### cURL ```bash curl -X POST http://localhost:4000/api/logs/upload \ -F "uid=player_001" \ -F "nickname=玩家昵称" \ -F "timestamp=$(date +%s)000" \ -F "file=@error.log" ``` ### JavaScript(Web 端) ```javascript const formData = new FormData(); formData.append('uid', 'player_001'); formData.append('nickname', '玩家昵称'); formData.append('timestamp', Date.now().toString()); formData.append('file', new Blob([errorLog], { type: 'text/plain' }), 'error.log'); fetch('http://localhost:4000/api/logs/upload', { method: 'POST', body: formData, }); ``` ## 前端功能 ### 主要页面 - **日志列表页** (`/`) - 表格展示:ID、玩家 UID、昵称、上报时间、文件大小 - 搜索筛选:按玩家 UID 搜索、按时间范围筛选(日期选择器) - 排序功能:按时间/UID 升序/降序切换 - 分页控制:支持自定义每页条数(10/20/50/100) - 操作按钮:查看详情、删除日志 - **日志详情页** (`/log/:id`) - 元信息展示:玩家 UID、昵称、上报时间、文件大小 - 日志内容:完整的日志文本内容(支持滚动查看) - 快捷操作:返回列表、删除当前日志 ### UI 组件 - 使用 Ant Design 5 组件库 - 响应式布局,支持桌面端和大屏设备 - 清爽的蓝白配色主题 ## 数据存储 ### 存储结构 日志数据存储在 `server/logs/` 目录下: ``` server/logs/ ├── index.json # 日志索引文件(元数据) ├── log_xxx_xxx.txt # 日志内容文件 └── log_yyy_yyy.txt ``` ### 索引文件格式(index.json) ```json [ { "id": "log_1707100000000_abc123", "uid": "player_001", "nickname": "玩家昵称", "timestamp": 1707100000000, "filename": "log_1707100000000_abc123.txt", "size": 1024, "createdAt": 1707100000000 } ] ``` ### 日志文件命名规则 格式:`log_{timestamp}_{randomId}.txt` - `timestamp`:上传时间戳(毫秒) - `randomId`:8位随机字符串 ## 🚀 开发指南 ### 目录说明 ``` report-admin/ ├── server/ # 后端目录 │ ├── src/ │ │ ├── index.ts # Express 服务器入口 │ │ ├── routes/logs.ts # 日志相关路由 │ │ ├── services/logService.ts # 日志业务逻辑 │ │ └── types/index.ts # TypeScript 类型定义 │ ├── logs/ # 日志文件存储目录 │ │ ├── index.json # 索引文件 │ │ └── *.txt # 日志文件 │ ├── tsconfig.json │ └── package.json │ ├── client/ # 前端目录 │ ├── src/ │ │ ├── App.tsx # 主应用组件 │ │ ├── main.tsx # 应用入口 │ │ ├── pages/ │ │ │ ├── LogList.tsx # 日志列表页 │ │ │ └── LogDetail.tsx # 日志详情页 │ │ ├── api/logs.ts # API 请求封装 │ │ └── types/index.ts # 类型定义 │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json │ └── README.md ``` ### 后端开发 **添加新接口:** 1. 在 `server/src/routes/logs.ts` 添加路由 2. 在 `server/src/services/logService.ts` 添加业务逻辑 3. 更新 `server/src/types/index.ts` 类型定义 **常用命令:** ```bash npm run dev # 开发模式(热重载) npm run build # 构建生产版本 npm start # 启动生产服务器 ``` ### 前端开发 **添加新页面:** 1. 在 `client/src/pages/` 创建页面组件 2. 在 `client/src/App.tsx` 添加路由 3. 在 `client/src/api/logs.ts` 添加 API 调用(如需要) **常用命令:** ```bash npm run dev # 启动开发服务器 npm run build # 构建生产版本 npm run preview # 预览生产构建 ``` ## 📝 注意事项 - 日志文件默认存储在本地文件系统,适合小规模使用 - 生产环境建议: - 使用数据库(如 PostgreSQL、MySQL)替代文件存储 - 添加身份认证和权限管理 - 实现日志归档和清理策略 - 配置 CORS 和请求限流 - 使用 HTTPS 加密传输 - 日志文件大小限制默认为 10MB,可在后端配置调整 - 建议定期备份 `server/logs/` 目录 ## 🔧 扩展建议 ### 短期优化 - [ ] 添加日志搜索功能(全文搜索) - [ ] 实现日志分类标签 - [ ] 添加数据导出功能(CSV、JSON) - [ ] 增加日志统计面板(图表展示) ### 长期规划 - [ ] 集成数据库(Prisma + PostgreSQL) - [ ] 添加用户系统(登录、权限管理) - [ ] 实现日志聚合和错误分组(类似 Sentry) - [ ] 添加告警通知功能(邮件、钉钉、企业微信) - [ ] 支持日志批量上传 - [ ] 实现日志实时监控(WebSocket) ## 📄 许可证 MIT License ## 🤝 贡献 欢迎提交 Issue 和 Pull Request!