36 KiB
System 基础模块文档
概述
System 基础模块提供了完整的系统管理功能,包括系统配置、数据字典、操作日志、任务管理、城市数据和文件上传等功能。
控制器结构
app/Http/Controllers/System/
├── Admin/ # 后台管理控制器
│ ├── Config.php # 系统配置控制器
│ ├── Log.php # 操作日志控制器
│ ├── Dictionary.php # 数据字典控制器
│ ├── Task.php # 任务管理控制器
│ ├── City.php # 城市数据控制器
│ └── Upload.php # 文件上传控制器
└── Api/ # 公共API控制器
├── Config.php # 系统配置API
├── Dictionary.php # 数据字典API
├── City.php # 城市数据API
└── Upload.php # 文件上传API
Admin 控制器
命名空间: App\Http\Controllers\System\Admin
路由前缀: /admin
中间件: auth.check:admin (后台管理认证)
Api 控制器
命名空间: App\Http\Controllers\System\Api
路由前缀: /api
中间件: auth:api (API认证,部分接口为公开接口)
技术栈
- PHP 8.1+
- Laravel 11
- Redis 缓存
- Intervention Image (图像处理)
- Laravel-S / Swoole (WebSocket 通知)
- WebSocket (实时消息推送)
数据库表结构
system_setting (系统配置表)
id: 主键group: 配置分组(如:system, site, upload)key: 配置键(唯一)value: 配置值(JSON格式)type: 数据类型(string, number, boolean, array, image, file)name: 配置名称description: 配置描述options: 可选值(JSON数组)sort: 排序status: 状态(0:禁用, 1:启用)created_at,updated_at: 时间戳
system_dictionaries (数据字典分类表)
id: 主键name: 字典名称code: 字典编码(唯一)description: 描述sort: 排序status: 状态created_at,updated_at: 时间戳
system_dictionary_items (数据字典项表)
id: 主键dictionary_id: 字典IDlabel: 显示标签value: 实际值sort: 排序status: 状态created_at,updated_at: 时间戳
system_logs (操作日志表)
id: 主键user_id: 用户IDusername: 用户名module: 模块action: 操作method: 请求方法 (GET/POST/PUT/DELETE)url: 请求URLip: IP地址user_agent: 用户代理params: 请求参数(JSON)result: 响应结果(仅错误时记录)status_code: HTTP状态码status: 状态 (success/error)error_message: 错误信息execution_time: 执行时间(毫秒)created_at: 创建时间updated_at: 更新时间
system_tasks (任务表)
id: 主键name: 任务名称code: 任务编码(唯一)command: 命令description: 描述cron_expression: Cron表达式status: 状态(0:禁用, 1:启用, 2:运行中, 3:失败)last_run_at: 最后运行时间next_run_at: 下次运行时间run_count: 运行次数fail_count: 失败次数last_message: 最后运行消息sort: 排序created_at,updated_at: 时间戳
system_cities (城市表)
id: 主键name: 城市名称code: 城市编码level: 级别(1:省, 2:市, 3:区县)parent_id: 父级IDadcode: 行政区划编码sort: 排序status: 状态created_at,updated_at: 时间戳
Admin API 接口文档
系统配置管理
获取配置列表
- 接口:
GET /admin/configs - 参数:
page: 页码(默认1)page_size: 每页数量(默认20)keyword: 搜索关键词group: 配置分组status: 状态order_by,order_direction: 排序
获取所有配置分组
- 接口:
GET /admin/configs/groups
按分组获取配置
- 接口:
GET /admin/configs/all - 参数:
group: 配置分组(可选)
获取配置详情
- 接口:
GET /admin/configs/{id}
创建配置
- 接口:
POST /admin/configs - 参数:
{ "group": "site", "key": "site_name", "name": "网站名称", "description": "网站显示的名称", "type": "string", "value": "\"我的网站\"", "options": null, "sort": 1, "status": 1 } - 数据类型说明:
string: 字符串number: 数字boolean: 布尔值array: 数组image: 图片file: 文件
更新配置
- 接口:
PUT /admin/configs/{id}
删除配置
- 接口:
DELETE /admin/configs/{id}
批量删除配置
- 接口:
POST /admin/configs/batch-delete - 参数:
{ "ids": [1, 2, 3] }
批量更新配置状态
- 接口:
POST /admin/configs/batch-status - 参数:
{ "ids": [1, 2, 3], "status": 1 }
操作日志管理
操作日志模块通过中间件自动记录所有后台管理 API 请求,实现全自动化的日志记录功能。
中间件说明
LogRequestMiddleware
位置: app/Http/Middleware/LogRequestMiddleware.php
功能:
- 自动拦截所有经过的请求
- 记录请求和响应信息
- 计算请求执行时间
- 提取用户信息和操作详情
- 过滤敏感参数(password、token、secret、key)
- 获取客户端真实 IP(支持代理)
- 异常处理,记录失败不影响业务
使用方式:
// 在路由中应用
Route::middleware(['auth.check:admin', 'log.request'])->group(function () {
// 需要记录日志的路由
});
日志记录规则
自动记录的请求:
所有经过 log.request 中间件的请求都会被自动记录,包括:
- 用户管理操作
- 角色管理操作
- 权限管理操作
- 部门管理操作
- 系统配置操作
- 其他所有后台管理操作
不记录的请求:
- 登录接口 (
POST /admin/auth/login) - 健康检查接口 (
GET /up) - 其他明确排除的路由
敏感信息过滤:
以下字段会被自动过滤,记录为 ******:
passwordpassword_confirmationtokensecretkey
错误日志处理:
- 成功请求 (HTTP 状态码 < 400):
status=success - 失败请求 (HTTP 状态码 >= 400):
status=error - 错误时记录响应内容和错误消息
- 同时写入 Laravel 日志文件 (
storage/logs/laravel.log)
获取日志列表
- 接口:
GET /admin/logs - 参数:
{ "user_id": 1, "username": "admin", "module": "users", "action": "创建 users", "status": "success", "start_date": "2024-01-01", "end_date": "2024-12-31", "ip": "192.168.1.1", "page": 1, "page_size": 20 } - 响应示例:
{ "code": 200, "message": "success", "data": { "list": [ { "id": 1, "user_id": 1, "username": "admin", "module": "users", "action": "创建 users", "method": "POST", "url": "http://example.com/admin/users", "ip": "192.168.1.1", "user_agent": "Mozilla/5.0...", "params": { "name": "test", "email": "test@example.com" }, "result": null, "status_code": 200, "status": "success", "error_message": null, "execution_time": 125, "created_at": "2024-01-01 12:00:00" } ], "total": 100, "page": 1, "page_size": 20 } }
获取日志详情
- 接口:
GET /admin/logs/{id} - 响应示例:
{ "code": 200, "message": "success", "data": { "id": 1, "user_id": 1, "username": "admin", "module": "users", "action": "创建 users", "method": "POST", "url": "http://example.com/admin/users", "ip": "192.168.1.1", "user_agent": "Mozilla/5.0...", "params": { "name": "test", "email": "test@example.com" }, "result": null, "status_code": 200, "status": "success", "error_message": null, "execution_time": 125, "created_at": "2024-01-01 12:00:00", "user": { "id": 1, "name": "管理员", "username": "admin" } } }
获取日志统计
- 接口:
GET /admin/logs/statistics - 参数:
{ "start_date": "2024-01-01", "end_date": "2024-12-31" } - 响应示例:
{ "code": 200, "message": "success", "data": { "total": 1000, "success": 950, "error": 50 } }
导出日志
- 接口:
POST /admin/logs/export - 参数: 与获取日志列表相同的查询参数
- 响应: Excel 文件下载
- 文件名格式:
系统操作日志_YYYYMMDDHHmmss.xlsx - 包含字段:
- ID
- 用户名
- 模块
- 操作
- 请求方法
- URL
- IP 地址
- 状态码
- 状态
- 错误信息
- 执行时间(ms)
- 创建时间
删除单条日志
- 接口:
DELETE /admin/logs/{id} - 响应示例:
{ "code": 200, "message": "删除成功", "data": null }
批量删除日志
- 接口:
POST /admin/logs/batch-delete - 参数:
{ "ids": [1, 2, 3, 4, 5] } - 响应示例:
{ "code": 200, "message": "批量删除成功", "data": null }
清理历史日志
- 接口:
POST /admin/logs/clear - 参数:
{ "days": 30 } - 说明: 清理指定天数前的所有日志记录,默认清理 30 天前的数据
- 响应示例:
{ "code": 200, "message": "清理成功", "data": null }
数据字典管理
数据字典模块提供了完整的字典管理功能,包括字典分类和字典项的 CRUD 操作。通过 WebSocket 实现了前后端缓存的实时同步更新。
字典缓存更新机制
概述
字典缓存更新机制通过 WebSocket 实现前后端字典缓存的实时同步,确保在字典分类和字典项的增删改等操作后,前端字典缓存能够自动更新。
技术实现
- 后端实现
在 app/Services/System/DictionaryService.php 中添加了 WebSocket 通知功能:
通知方法:
-
notifyDictionaryUpdate- 字典分类更新通知- 触发时机:创建、更新、删除、批量删除、批量更新状态
- 消息类型:
dictionary_update
-
notifyDictionaryItemUpdate- 字典项更新通知- 触发时机:创建、更新、删除、批量删除、批量更新状态
- 消息类型:
dictionary_item_update
WebSocket 消息格式:
字典分类更新消息:
{
"type": "dictionary_update",
"data": {
"action": "create|update|delete|batch_delete|batch_update_status",
"resource_type": "dictionary",
"data": {
// 字典分类数据
},
"timestamp": 1234567890
}
}
字典项更新消息:
{
"type": "dictionary_item_update",
"data": {
"action": "create|update|delete|batch_delete|batch_update_status",
"resource_type": "dictionary_item",
"data": {
// 字典项数据
},
"timestamp": 1234567890
}
}
- 前端实现
创建了 resources/admin/src/composables/useWebSocket.js 来处理 WebSocket 连接和消息监听:
主要功能:
- 初始化 WebSocket 连接(检查用户登录状态、验证用户信息完整性)
- 消息处理器:
handleDictionaryUpdate和handleDictionaryItemUpdate - 缓存刷新:接收到更新通知后,自动刷新字典缓存并显示成功提示
App.vue 集成:
onMounted(async () => {
// 初始化 WebSocket 连接
if (userStore.isLoggedIn()) {
initWebSocket()
}
})
onUnmounted(() => {
// 关闭 WebSocket 连接
closeWebSocket()
})
工作流程:
用户操作(增删改字典)
↓
后端 Controller 调用 Service
↓
Service 执行数据库操作
↓
Service 清理后端缓存(Redis)
↓
Service 发送 WebSocket 广播通知
↓
WebSocket 推送消息到所有在线客户端
↓
前端接收 WebSocket 消息
↓
触发相应的消息处理器
↓
刷新前端字典缓存
↓
显示成功提示
注意事项:
- WebSocket 仅在用户登录后建立连接
- 连接失败会自动重试(最多 5 次)
- 页面卸载时会自动关闭连接
- WebSocket 通知功能依赖于 Laravel-S (Swoole) 环境
- 在普通 PHP 环境下运行时,WebSocket 通知会优雅降级(不发送通知,但不影响功能)
获取字典列表
- 接口:
GET /admin/dictionaries - 参数:
page,page_size,keyword,status,order_by,order_direction
获取所有字典
- 接口:
GET /admin/dictionaries/all
获取字典详情
- 接口:
GET /admin/dictionaries/{id}
创建字典
- 接口:
POST /admin/dictionaries - 参数:
{ "name": "用户状态", "code": "user_status", "description": "用户状态字典", "sort": 1, "status": 1 }
更新字典
- 接口:
PUT /admin/dictionaries/{id}
删除字典
- 接口:
DELETE /admin/dictionaries/{id}
批量删除字典
- 接口:
POST /admin/dictionaries/batch-delete
批量更新字典状态
- 接口:
POST /admin/dictionaries/batch-status
数据字典项管理
获取字典项列表
- 接口:
GET /admin/dictionary-items - 参数:
page,page_sizedictionary_id: 字典ID(必需)keyword: 搜索关键词status,order_by,order_direction
创建字典项
- 接口:
POST /admin/dictionary-items - 参数:
{ "dictionary_id": 1, "label": "正常", "value": "1", "sort": 1, "status": 1 }
更新字典项
- 接口:
PUT /admin/dictionary-items/{id}
删除字典项
- 接口:
DELETE /admin/dictionary-items/{id}
批量删除字典项
- 接口:
POST /admin/dictionary-items/batch-delete
批量更新字典项状态
- 接口:
POST /admin/dictionary-items/batch-status
任务管理
获取任务列表
- 接口:
GET /admin/tasks - 参数:
page,page_size,keyword,status,order_by,order_direction
获取所有任务
- 接口:
GET /admin/tasks/all
获取任务详情
- 接口:
GET /admin/tasks/{id}
创建任务
- 接口:
POST /admin/tasks - 参数:
{ "name": "清理临时文件", "code": "cleanup_temp_files", "command": "cleanup:temp", "description": "每天清理过期临时文件", "cron_expression": "0 2 * * *", "sort": 1, "status": 1 } - Cron表达式说明:
- 格式:
分 时 日 月 周 - 示例:
0 2 * * *(每天凌晨2点执行) - 示例:
*/5 * * * *(每5分钟执行一次)
- 格式:
更新任务
- 接口:
PUT /admin/tasks/{id}
删除任务
- 接口:
DELETE /admin/tasks/{id}
批量删除任务
- 接口:
POST /admin/tasks/batch-delete
批量更新任务状态
- 接口:
POST /admin/tasks/batch-status
手动执行任务
- 接口:
POST /admin/tasks/{id}/run - 说明: 立即执行指定任务
获取任务统计
- 接口:
GET /admin/tasks/statistics - 返回:
{ "code": 200, "message": "success", "data": { "total": 10, "enabled": 8, "disabled": 2, "running": 0, "failed": 0 } }
城市数据管理
获取城市列表
- 接口:
GET /admin/cities - 参数:
page,page_size,keyword,level,parent_id,statusorder_by,order_direction
获取城市树
- 接口:
GET /admin/cities/tree - 说明: 从缓存中获取完整的三级城市树
获取城市详情
- 接口:
GET /admin/cities/{id}
获取子级城市
- 接口:
GET /admin/cities/{id}/children - 说明: 获取指定城市的下级城市
获取所有省份
- 接口:
GET /admin/cities/provinces
获取指定省份的城市
- 接口:
GET /admin/cities/{provinceId}/cities
获取指定城市的区县
- 接口:
GET /admin/cities/{cityId}/districts
创建城市
- 接口:
POST /admin/cities - 参数:
{ "name": "北京市", "code": "110000", "level": 1, "parent_id": 0, "adcode": "110000", "sort": 1, "status": 1 } - 级别说明:
1: 省级2: 市级3: 区县级
更新城市
- 接口:
PUT /admin/cities/{id}
删除城市
- 接口:
DELETE /admin/cities/{id}
批量删除城市
- 接口:
POST /admin/cities/batch-delete
批量更新城市状态
- 接口:
POST /admin/cities/batch-status
文件上传管理
单文件上传
- 接口:
POST /admin/upload - 参数:
file: 文件(multipart/form-data,最大10MB)directory: 存储目录(默认:uploads)compress: 是否压缩图片(默认:false)quality: 图片质量(1-100,默认:80)width: 压缩宽度(可选)height: 压缩高度(可选)
- 返回:
{ "code": 200, "message": "上传成功", "data": { "url": "http://example.com/uploads/2024/01/xxx.jpg", "path": "uploads/2024/01/xxx.jpg", "name": "xxx.jpg", "size": 102400, "mime_type": "image/jpeg" } }
多文件上传
- 接口:
POST /admin/upload/multiple - 参数:
files: 文件数组(multipart/form-data)- 其他参数同单文件上传
- 返回: 文件数组
Base64上传
- 接口:
POST /admin/upload/base64 - 参数:
{ "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...", "directory": "uploads", "file_name": "image.jpg" }
删除文件
- 接口:
POST /admin/upload/delete - 参数:
{ "path": "uploads/2024/01/xxx.jpg" }
批量删除文件
- 接口:
POST /admin/upload/batch-delete - 参数:
{ "paths": [ "uploads/2024/01/xxx.jpg", "uploads/2024/01/yyy.jpg" ] }
Public API 接口文档
系统配置 API
获取所有配置
- 接口:
GET /api/system/configs - 认证: 公开接口(无需认证)
- 返回:
{ "code": 200, "message": "success", "data": { "site_name": "我的网站", "site_logo": "/uploads/logo.png" } }
按分组获取配置
- 接口:
GET /api/system/configs/group - 参数:
group: 配置分组
- 认证: 公开接口
根据键获取配置
- 接口:
GET /api/system/configs/key - 参数:
key: 配置键
- 认证: 公开接口
数据字典 API
获取所有字典
- 接口:
GET /api/system/dictionaries - 认证: 公开接口
- 返回:
{ "code": 200, "message": "success", "data": [ { "id": 1, "code": "user_status", "name": "用户状态", "items": [...] } ] }
根据编码获取字典项
- 接口:
GET /api/system/dictionaries/code - 参数:
code: 字典编码
- 认证: 公开接口
- 返回:
{ "code": 200, "message": "success", "data": { "code": "user_status", "items": [ {"label": "正常", "value": "1"}, {"label": "禁用", "value": "0"} ] } }
获取字典详情
- 接口:
GET /api/system/dictionaries/{id} - 认证: 公开接口
城市数据 API
获取城市树
- 接口:
GET /api/system/cities/tree - 认证: 公开接口
- 说明: 从缓存中获取完整的三级城市树
获取所有省份
- 接口:
GET /api/system/cities/provinces - 认证: 公开接口
获取指定省份的城市
- 接口:
GET /api/system/cities/{provinceId}/cities - 认证: 公开接口
获取指定城市的区县
- 接口:
GET /api/system/cities/{cityId}/districts - 认证: 公开接口
获取城市详情
- 接口:
GET /api/system/cities/{id} - 认证: 公开接口
文件上传 API
单文件上传
- 接口:
POST /api/system/upload - 认证: 需要认证(
auth:api) - 参数: 同Admin上传接口
多文件上传
- 接口:
POST /api/system/upload/multiple - 认证: 需要认证(
auth:api) - 参数: 同Admin上传接口
Base64上传
- 接口:
POST /api/system/upload/base64 - 认证: 需要认证(
auth:api) - 参数: 同Admin上传接口
缓存机制
城市数据缓存
- 缓存键:
city:tree - 过期时间: 永久(手动清除)
- 更新时机: 城市数据增删改时自动清除
系统配置缓存
- 缓存键:
config:all或config:group:{group} - 过期时间: 60分钟
- 更新时机: 配置数据增删改时自动清除
数据字典缓存
- 缓存键:
dictionary:all或dictionary:code:{code} - 过期时间: 3600秒(1小时)
- 更新时机:
- 字典数据增删改时自动清除后端缓存(Redis)
- 通过 WebSocket 通知前端自动刷新缓存
字典缓存同步流程:
- 后端执行字典操作(增删改)
- 清理 Redis 缓存
- 发送 WebSocket 广播通知
- 前端接收通知并自动刷新缓存
- 显示成功提示
服务层说明
ConfigService
提供系统配置的CRUD操作和缓存管理。
主要方法:
getList(): 获取配置列表getByGroup(): 按分组获取配置getConfigValue(): 获取配置值create(): 创建配置update(): 更新配置delete(): 删除配置
LogService
提供操作日志的记录、查询和清理功能。
主要方法:
getList(): 获取日志列表getById(): 根据 ID 获取日志详情getStatistics(): 获取统计数据getListQuery(): 获取日志查询构建器clearLogs(): 清理过期日志delete(): 删除单条日志batchDelete(): 批量删除日志record(): 记录日志(由中间件自动调用)
特性:
- 自动记录所有经过中间件的请求
- 计算请求执行时间
- 过滤敏感参数
- 获取客户端真实 IP(支持代理)
- 异常处理,记录失败不影响业务
DictionaryService
提供数据字典和字典项的管理功能,包括 WebSocket 通知机制。
主要方法:
getList(): 获取字典列表getItemsByCode(): 按编码获取字典项create(): 创建字典createItem(): 创建字典项update(): 更新字典updateItem(): 更新字典项notifyDictionaryUpdate(): 发送字典更新通知notifyDictionaryItemUpdate(): 发送字典项更新通知
缓存机制:
- Redis 缓存字典数据(TTL: 3600秒)
- WebSocket 实时通知前端更新
- 前端 Pinia + 本地存储持久化
TaskService
提供任务的管理和执行功能。
主要方法:
getList(): 获取任务列表run(): 手动执行任务getStatistics(): 获取任务统计updateStatus(): 更新任务状态
CityService
提供城市数据的管理和缓存功能。
主要方法:
getList(): 获取城市列表getCachedTree(): 从缓存获取城市树getProvinces(): 获取省份列表getCities(): 获取城市列表getDistricts(): 获取区县列表
UploadService
提供文件上传和删除功能。
主要方法:
upload(): 单文件上传uploadMultiple(): 多文件上传uploadBase64(): Base64上传delete(): 删除文件compressImage(): 图片压缩
初始化数据
# 执行迁移
php artisan migrate
# 填充初始数据
php artisan db:seed --class=SystemSeeder
初始数据包括:
- 基础系统配置
- 常用数据字典
- 全国省市区数据
前端集成示例
日志管理页面
<template>
<a-card title="操作日志">
<!-- 搜索表单 -->
<a-form layout="inline" :model="searchParams">
<a-form-item label="用户名">
<a-input v-model:value="searchParams.username" placeholder="请输入用户名" />
</a-form-item>
<a-form-item label="模块">
<a-input v-model:value="searchParams.module" placeholder="请输入模块名" />
</a-form-item>
<a-form-item label="状态">
<a-select v-model:value="searchParams.status" placeholder="请选择状态">
<a-select-option value="success">成功</a-select-option>
<a-select-option value="error">失败</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
<a-button @click="handleExport">导出</a-button>
</a-form-item>
</a-form>
<!-- 数据表格 -->
<a-table
:columns="columns"
:data-source="logs"
:loading="loading"
:pagination="pagination"
@change="handleTableChange"
>
<template #status="{ record }">
<a-tag :color="record.status === 'success' ? 'green' : 'red'">
{{ record.status === 'success' ? '成功' : '失败' }}
</a-tag>
</template>
<template #action="{ record }">
<a-button type="link" @click="handleView(record)">查看</a-button>
<a-button type="link" danger @click="handleDelete(record.id)">删除</a-button>
</template>
</a-table>
</a-card>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import request from '@/utils/request'
const logs = ref([])
const loading = ref(false)
const searchParams = reactive({
username: '',
module: '',
status: null,
page: 1,
page_size: 20
})
const pagination = reactive({
total: 0,
current: 1,
pageSize: 20
})
const columns = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '用户名', dataIndex: 'username', width: 120 },
{ title: '模块', dataIndex: 'module', width: 100 },
{ title: '操作', dataIndex: 'action', width: 150 },
{ title: '请求方法', dataIndex: 'method', width: 100 },
{ title: 'IP 地址', dataIndex: 'ip', width: 150 },
{ title: '状态', dataIndex: 'status', slots: { customRender: 'status' }, width: 100 },
{ title: '执行时间', dataIndex: 'execution_time', width: 100 },
{ title: '创建时间', dataIndex: 'created_at', width: 180 },
{ title: '操作', slots: { customRender: 'action' }, width: 150, fixed: 'right' }
]
// 获取日志列表
const fetchLogs = async () => {
loading.value = true
try {
const res = await request.get('/admin/logs', { params: searchParams })
logs.value = res.data.list
pagination.total = res.data.total
pagination.current = res.data.page
pagination.pageSize = res.data.page_size
} catch (error) {
message.error('获取日志失败')
} finally {
loading.value = false
}
}
// 查询
const handleSearch = () => {
searchParams.page = 1
fetchLogs()
}
// 重置
const handleReset = () => {
searchParams.username = ''
searchParams.module = ''
searchParams.status = null
searchParams.page = 1
fetchLogs()
}
// 导出
const handleExport = async () => {
try {
const res = await request.post('/admin/logs/export', searchParams, {
responseType: 'blob'
})
const url = window.URL.createObjectURL(new Blob([res]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `操作日志_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
message.success('导出成功')
} catch (error) {
message.error('导出失败')
}
}
// 查看详情
const handleView = (record) => {
// 打开详情对话框
console.log('查看日志', record)
}
// 删除
const handleDelete = async (id) => {
try {
await request.delete(`/admin/logs/${id}`)
message.success('删除成功')
fetchLogs()
} catch (error) {
message.error('删除失败')
}
}
// 表格分页变化
const handleTableChange = (pag) => {
searchParams.page = pag.current
searchParams.page_size = pag.pageSize
fetchLogs()
}
onMounted(() => {
fetchLogs()
})
</script>
注意事项
1. Swoole环境注意事项
- 文件上传时注意临时文件清理
- 使用Redis缓存避免内存泄漏
- 图片压缩使用协程安全的方式
- WebSocket 通知依赖于 Laravel-S 环境
2. 安全注意事项
- 文件上传必须验证文件类型和大小
- 敏感操作必须记录日志
- 配置数据不要存储密码等敏感信息
- 日志敏感信息已自动过滤
3. 性能优化
- 城市数据使用Redis缓存
- 大量日志数据定期清理
- 图片上传时进行压缩处理
- 日志记录在请求处理后执行,不影响响应速度
4. 文件上传
- 限制文件上传大小
- 验证文件MIME类型
- 定期清理临时文件
5. 日志管理
- 定期清理历史日志(建议使用任务调度器)
- 确保查询字段有索引
- 使用分页查询避免加载过多数据
性能优化建议
1. 定期清理日志
建议使用 Laravel 任务调度器定期清理历史日志:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// 每天凌晨 2 点清理 90 天前的日志
$schedule->call(function () {
app(LogService::class)->clearLogs(90);
})->dailyAt('02:00');
}
2. 数据库索引
确保以下字段有索引:
system_logs:user_id,username,module,status,created_atsystem_dictionaries:code,statussystem_dictionary_items:dictionary_id,statussystem_setting:group,key,status
3. 分页查询
列表查询必须使用分页,避免一次加载过多数据。
4. 异步记录
日志记录操作应放在请求处理后,不影响响应速度。
5. 细粒度缓存更新
字典缓存当前实现为全量刷新,未来可以优化为增量更新:
// 只更新受影响的字典
async function handleDictionaryUpdate(data) {
const { action, data: dictData } = data
if (action === 'update' && dictData.code) {
// 只更新特定的字典
await dictionaryStore.getDictionary(dictData.code, true)
} else {
// 全量刷新
await dictionaryStore.refresh(true)
}
}
扩展建议
1. 日志告警
添加日志异常告警功能,当出现大量错误日志时自动通知管理员。
2. 配置加密
敏感配置数据加密存储,提高安全性。
3. 多语言
支持配置数据的多语言,便于国际化部署。
4. 任务监控
添加任务执行监控和通知,实时掌握任务运行状态。
5. CDN集成
文件上传支持CDN分发,提高访问速度。
6. WebSocket 权限控制
可以只向有权限的用户发送通知:
// 后端只向有字典管理权限的用户发送
$adminUserIds = User::whereHas('roles', function($query) {
$query->where('name', 'admin');
})->pluck('id')->toArray();
$this->webSocketService->sendToUsers($adminUserIds, $message);
7. 消息队列
对于高并发场景,可以使用消息队列异步发送 WebSocket 通知:
// 使用 Laravel 队列
UpdateDictionaryCacheJob::dispatch($action, $data);
常见问题
Q1: 如何清除城市数据缓存?
A: 调用 CityService::clearCache() 方法或运行 php artisan cache:forget city:tree。
Q2: 图片上传后如何压缩?
A: 上传时设置 compress=true 和 quality 参数,系统会自动压缩。
Q3: 如何配置定时任务?
A: 在Admin后台创建任务,设置Cron表达式,系统会自动调度执行。
Q4: 数据字典如何使用?
A: 通过Public API获取字典数据,前端根据数据渲染下拉框等组件。
Q5: 日志数据过多如何处理?
A: 定期使用 /admin/logs/clear 接口清理过期日志,或在后台设置自动清理任务。
Q6: 为什么某些请求没有被记录?
A: 检查路由是否应用了 log.request 中间件,或者在中间件中是否被排除了。
Q7: 字典缓存未更新怎么办?
A: 检查以下几点:
- 确认 Laravel-S 服务是否启动:
php bin/laravels status - 检查浏览器控制台是否有 WebSocket 错误
- 确认用户已登录且有 token
- 手动刷新页面验证 API 是否正常
Q8: WebSocket 连接失败会影响功能吗?
A: 不会。WebSocket 连接失败不影响页面正常使用,只是不会收到自动更新通知。字典数据仍会正常更新到数据库和 Redis 缓存,只是前端不会收到实时通知,需要手动刷新页面。
测试建议
1. 功能测试
- 测试各种请求是否被正确记录
- 测试敏感信息是否被正确过滤
- 测试日志查询和筛选功能
- 测试日志导出功能
- 测试批量删除和清理功能
- 测试字典 WebSocket 通知是否正确
2. 性能测试
- 测试日志记录对响应时间的影响
- 测试大量日志数据的查询性能
- 测试并发写入的性能
- 测试 WebSocket 广播性能
3. 集成测试(字典 WebSocket)
- 启动后端服务(Laravel-S)
- 启动前端开发服务器
- 在浏览器中登录系统
- 打开开发者工具的 Network -> WS 标签查看 WebSocket 消息
- 执行字典增删改操作
- 验证:
- WebSocket 消息是否正确接收
- 缓存是否自动刷新
- 页面数据是否更新
- 提示消息是否显示
4. 并发测试
- 打开多个浏览器窗口并登录
- 在一个窗口中进行字典操作
- 验证所有窗口的缓存是否同步更新
5. 边界测试
- 测试异常情况下的日志记录
- 测试超长参数的处理
- 测试特殊字符的处理
- 测试 WebSocket 断连重连机制
文件清单
核心文件
控制器:
app/Http/Controllers/System/Admin/Config.php
app/Http/Controllers/System/Admin/Log.php
app/Http/Controllers/System/Admin/Dictionary.php
app/Http/Controllers/System/Admin/Task.php
app/Http/Controllers/System/Admin/City.php
app/Http/Controllers/System/Admin/Upload.php
app/Http/Controllers/System/WebSocket.php
中间件:
app/Http/Middleware/LogRequestMiddleware.php
请求验证:
app/Http/Requests/LogRequest.php
服务层:
app/Services/System/ConfigService.php
app/Services/System/LogService.php
app/Services/System/DictionaryService.php
app/Services/System/TaskService.php
app/Services/System/CityService.php
app/Services/System/UploadService.php
app/Services/WebSocket/WebSocketService.php
模型:
app/Models/System/Config.php
app/Models/System/Log.php
app/Models/System/Dictionary.php
app/Models/System/DictionaryItem.php
app/Models/System/Task.php
app/Models/System/City.php
路由:
routes/admin.php (后台管理路由)
routes/api.php (公共 API 路由)
前端:
resources/admin/src/composables/useWebSocket.js
resources/admin/src/App.vue (集成 WebSocket)
文档:
docs/README_SYSTEM.md (本文档)
总结
System 基础模块提供了完整的系统管理功能,包括:
核心功能
- ✅ 系统配置管理(多分组、多类型支持)
- ✅ 数据字典管理(分类 + 字典项)
- ✅ 操作日志管理(自动记录、多维度查询、导出)
- ✅ 任务管理(定时任务、手动执行、统计)
- ✅ 城市数据管理(三级联动、缓存优化)
- ✅ 文件上传管理(单文件、多文件、Base64、压缩)
高级特性
- ✅ WebSocket 实时通知(字典缓存自动更新)
- ✅ Redis 缓存机制(性能优化)
- ✅ 自动化日志记录(中间件拦截)
- ✅ 敏感信息保护(自动过滤)
- ✅ 数据导出功能(Excel 导出)
- ✅ 批量操作支持
- ✅ 完整的 API 文档
性能优化
- ✅ Redis 缓存(城市数据、系统配置、数据字典)
- ✅ 日志异步记录(不影响响应速度)
- ✅ 分页查询(避免加载过多数据)
- ✅ 图片压缩(减少存储空间)
- ✅ WebSocket 实时更新(减少不必要的请求)
安全特性
- ✅ 敏感信息过滤
- ✅ 文件类型验证
- ✅ 请求日志记录
- ✅ IP 地址记录
- ✅ 异常处理机制
System 模块现已完全集成到项目中,提供了完整的系统管理功能和优秀的用户体验。