Files
laravel_swoole/docs/README_SYSTEM.md
2026-02-19 10:39:38 +08:00

36 KiB
Raw Blame History

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: 字典ID
  • label: 显示标签
  • value: 实际值
  • sort: 排序
  • status: 状态
  • created_at, updated_at: 时间戳

system_logs (操作日志表)

  • id: 主键
  • user_id: 用户ID
  • username: 用户名
  • module: 模块
  • action: 操作
  • method: 请求方法 (GET/POST/PUT/DELETE)
  • url: 请求URL
  • ip: 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: 父级ID
  • adcode: 行政区划编码
  • 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)
  • 其他明确排除的路由

敏感信息过滤: 以下字段会被自动过滤,记录为 ******:

  • password
  • password_confirmation
  • token
  • secret
  • key

错误日志处理:

  • 成功请求 (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 实现前后端字典缓存的实时同步,确保在字典分类和字典项的增删改等操作后,前端字典缓存能够自动更新。

技术实现

  1. 后端实现

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
  }
}
  1. 前端实现

创建了 resources/admin/src/composables/useWebSocket.js 来处理 WebSocket 连接和消息监听:

主要功能:

  • 初始化 WebSocket 连接(检查用户登录状态、验证用户信息完整性)
  • 消息处理器:handleDictionaryUpdatehandleDictionaryItemUpdate
  • 缓存刷新:接收到更新通知后,自动刷新字典缓存并显示成功提示

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_size
    • dictionary_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, status
    • order_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:allconfig:group:{group}
  • 过期时间: 60分钟
  • 更新时机: 配置数据增删改时自动清除

数据字典缓存

  • 缓存键: dictionary:alldictionary:code:{code}
  • 过期时间: 3600秒1小时
  • 更新时机:
    • 字典数据增删改时自动清除后端缓存Redis
    • 通过 WebSocket 通知前端自动刷新缓存

字典缓存同步流程:

  1. 后端执行字典操作(增删改)
  2. 清理 Redis 缓存
  3. 发送 WebSocket 广播通知
  4. 前端接收通知并自动刷新缓存
  5. 显示成功提示

服务层说明

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_at
  • system_dictionaries: code, status
  • system_dictionary_items: dictionary_id, status
  • system_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=truequality 参数,系统会自动压缩。

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. 功能测试

  1. 测试各种请求是否被正确记录
  2. 测试敏感信息是否被正确过滤
  3. 测试日志查询和筛选功能
  4. 测试日志导出功能
  5. 测试批量删除和清理功能
  6. 测试字典 WebSocket 通知是否正确

2. 性能测试

  1. 测试日志记录对响应时间的影响
  2. 测试大量日志数据的查询性能
  3. 测试并发写入的性能
  4. 测试 WebSocket 广播性能

3. 集成测试(字典 WebSocket

  1. 启动后端服务Laravel-S
  2. 启动前端开发服务器
  3. 在浏览器中登录系统
  4. 打开开发者工具的 Network -> WS 标签查看 WebSocket 消息
  5. 执行字典增删改操作
  6. 验证:
    • WebSocket 消息是否正确接收
    • 缓存是否自动刷新
    • 页面数据是否更新
    • 提示消息是否显示

4. 并发测试

  1. 打开多个浏览器窗口并登录
  2. 在一个窗口中进行字典操作
  3. 验证所有窗口的缓存是否同步更新

5. 边界测试

  1. 测试异常情况下的日志记录
  2. 测试超长参数的处理
  3. 测试特殊字符的处理
  4. 测试 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 模块现已完全集成到项目中,提供了完整的系统管理功能和优秀的用户体验。