# 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`
- **参数**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "ids": [1, 2, 3]
  }
  ```

#### 批量更新配置状态
- **接口**: `POST /admin/configs/batch-status`
- **参数**:
  ```json
  {
    "ids": [1, 2, 3],
    "status": 1
  }
  ```

### 操作日志管理

操作日志模块通过中间件自动记录所有后台管理 API 请求，实现全自动化的日志记录功能。

#### 中间件说明

**LogRequestMiddleware**

位置: `app/Http/Middleware/LogRequestMiddleware.php`

功能:
- 自动拦截所有经过的请求
- 记录请求和响应信息
- 计算请求执行时间
- 提取用户信息和操作详情
- 过滤敏感参数（password、token、secret、key）
- 获取客户端真实 IP（支持代理）
- 异常处理，记录失败不影响业务

使用方式:
```php
// 在路由中应用
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`
- **参数**:
  ```json
  {
    "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
  }
  ```
- **响应示例**:
  ```json
  {
    "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}`
- **响应示例**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "start_date": "2024-01-01",
    "end_date": "2024-12-31"
  }
  ```
- **响应示例**:
  ```json
  {
    "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}`
- **响应示例**:
  ```json
  {
    "code": 200,
    "message": "删除成功",
    "data": null
  }
  ```

#### 批量删除日志
- **接口**: `POST /admin/logs/batch-delete`
- **参数**:
  ```json
  {
    "ids": [1, 2, 3, 4, 5]
  }
  ```
- **响应示例**:
  ```json
  {
    "code": 200,
    "message": "批量删除成功",
    "data": null
  }
  ```

#### 清理历史日志
- **接口**: `POST /admin/logs/clear`
- **参数**:
  ```json
  {
    "days": 30
  }
  ```
- **说明**: 清理指定天数前的所有日志记录，默认清理 30 天前的数据
- **响应示例**:
  ```json
  {
    "code": 200,
    "message": "清理成功",
    "data": null
  }
  ```

### 数据字典管理

数据字典模块提供了完整的字典管理功能，包括字典分类和字典项的 CRUD 操作。通过 WebSocket 实现了前后端缓存的实时同步更新。

#### 字典缓存更新机制

**概述**

字典缓存更新机制通过 WebSocket 实现前后端字典缓存的实时同步，确保在字典分类和字典项的增删改等操作后，前端字典缓存能够自动更新。

**技术实现**

1. **后端实现**

在 `app/Services/System/DictionaryService.php` 中添加了 WebSocket 通知功能：

**通知方法：**
- `notifyDictionaryUpdate` - 字典分类更新通知
  - 触发时机：创建、更新、删除、批量删除、批量更新状态
  - 消息类型：`dictionary_update`

- `notifyDictionaryItemUpdate` - 字典项更新通知
  - 触发时机：创建、更新、删除、批量删除、批量更新状态
  - 消息类型：`dictionary_item_update`

**WebSocket 消息格式：**

字典分类更新消息：
```json
{
  "type": "dictionary_update",
  "data": {
    "action": "create|update|delete|batch_delete|batch_update_status",
    "resource_type": "dictionary",
    "data": {
      // 字典分类数据
    },
    "timestamp": 1234567890
  }
}
```

字典项更新消息：
```json
{
  "type": "dictionary_item_update",
  "data": {
    "action": "create|update|delete|batch_delete|batch_update_status",
    "resource_type": "dictionary_item",
    "data": {
      // 字典项数据
    },
    "timestamp": 1234567890
  }
}
```

2. **前端实现**

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

**主要功能：**
- 初始化 WebSocket 连接（检查用户登录状态、验证用户信息完整性）
- 消息处理器：`handleDictionaryUpdate` 和 `handleDictionaryItemUpdate`
- 缓存刷新：接收到更新通知后，自动刷新字典缓存并显示成功提示

**App.vue 集成：**

```javascript
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`
- **参数**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "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`
- **返回**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "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`: 压缩高度（可选）
- **返回**:
  ```json
  {
    "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`
- **参数**:
  ```json
  {
    "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...",
    "directory": "uploads",
    "file_name": "image.jpg"
  }
  ```

#### 删除文件
- **接口**: `POST /admin/upload/delete`
- **参数**:
  ```json
  {
    "path": "uploads/2024/01/xxx.jpg"
  }
  ```

#### 批量删除文件
- **接口**: `POST /admin/upload/batch-delete`
- **参数**:
  ```json
  {
    "paths": [
      "uploads/2024/01/xxx.jpg",
      "uploads/2024/01/yyy.jpg"
    ]
  }
  ```

## Public API 接口文档

### 系统配置 API

#### 获取所有配置
- **接口**: `GET /api/system/configs`
- **认证**: 公开接口（无需认证）
- **返回**:
  ```json
  {
    "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`
- **认证**: 公开接口
- **返回**:
  ```json
  {
    "code": 200,
    "message": "success",
    "data": [
      {
        "id": 1,
        "code": "user_status",
        "name": "用户状态",
        "items": [...]
      }
    ]
  }
  ```

#### 根据编码获取字典项
- **接口**: `GET /api/system/dictionaries/code`
- **参数**:
  - `code`: 字典编码
- **认证**: 公开接口
- **返回**:
  ```json
  {
    "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 通知前端自动刷新缓存

**字典缓存同步流程：**
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()`: 图片压缩

## 初始化数据

```bash
# 执行迁移
php artisan migrate

# 填充初始数据
php artisan db:seed --class=SystemSeeder
```

初始数据包括：
- 基础系统配置
- 常用数据字典
- 全国省市区数据

## 前端集成示例

### 日志管理页面

```vue
<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 任务调度器定期清理历史日志:

```php
// 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. 细粒度缓存更新

字典缓存当前实现为全量刷新，未来可以优化为增量更新：

```javascript
// 只更新受影响的字典
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 权限控制
可以只向有权限的用户发送通知：

```php
// 后端只向有字典管理权限的用户发送
$adminUserIds = User::whereHas('roles', function($query) {
    $query->where('name', 'admin');
})->pluck('id')->toArray();

$this->webSocketService->sendToUsers($adminUserIds, $message);
```

### 7. 消息队列
对于高并发场景，可以使用消息队列异步发送 WebSocket 通知：

```php
// 使用 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. 功能测试
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 模块现已完全集成到项目中，提供了完整的系统管理功能和优秀的用户体验。
