初始化项目
This commit is contained in:
337
docs/LOG_IMPLEMENTATION_SUMMARY.md
Normal file
337
docs/LOG_IMPLEMENTATION_SUMMARY.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# 日志模块实现总结
|
||||
|
||||
## 实现概述
|
||||
|
||||
本次优化完善了后端日志模块,实现了自动化的请求日志记录功能,所有后台管理 API 请求都会被自动记录到数据库中。
|
||||
|
||||
## 实现内容
|
||||
|
||||
### 1. 新增文件
|
||||
|
||||
#### 中间件
|
||||
- **app/Http/Middleware/LogRequestMiddleware.php**
|
||||
- 自动拦截所有经过的请求
|
||||
- 记录请求和响应信息
|
||||
- 计算请求执行时间
|
||||
- 提取用户信息和操作详情
|
||||
- 自动过滤敏感参数(密码、token等)
|
||||
- 获取客户端真实 IP(支持代理)
|
||||
|
||||
#### 请求验证
|
||||
- **app/Http/Requests/LogRequest.php**
|
||||
- 统一的请求参数验证
|
||||
- 支持列表查询、批量删除、清理等操作的参数验证
|
||||
- 自定义错误消息
|
||||
- 自动设置默认值
|
||||
|
||||
#### 文档
|
||||
- **docs/README_LOG.md**
|
||||
- 完整的模块文档
|
||||
- API 接口说明
|
||||
- 数据库表结构
|
||||
- 使用示例
|
||||
- 前端集成代码
|
||||
- 常见问题解答
|
||||
|
||||
### 2. 修改文件
|
||||
|
||||
#### 控制器
|
||||
- **app/Http/Controllers/System/Admin/Log.php**
|
||||
- 添加 `export` 方法:支持导出日志数据为 Excel
|
||||
- 使用 `LogRequest` 进行参数验证
|
||||
- 优化响应格式
|
||||
|
||||
#### 服务层
|
||||
- **app/Services/System/LogService.php**
|
||||
- 添加 `getListQuery` 方法:提供查询构建器(用于导出等场景)
|
||||
- 新增 `buildQuery` 方法:统一的查询构建逻辑
|
||||
- 代码重构,减少重复代码
|
||||
|
||||
#### 路由配置
|
||||
- **routes/admin.php**
|
||||
- 添加 `POST /admin/logs/export` 导出路由
|
||||
- 在所有需要认证的路由组中应用 `log.request` 中间件
|
||||
|
||||
#### 中间件配置
|
||||
- **bootstrap/app.php**
|
||||
- 注册 `log.request` 中间件别名
|
||||
- 创建 `admin.log` 中间件组
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 自动日志记录
|
||||
- ✅ 所有后台管理 API 请求自动记录
|
||||
- ✅ 记录用户信息(ID、用户名)
|
||||
- ✅ 记录请求信息(方法、URL、参数)
|
||||
- ✅ 记录响应信息(状态码、执行时间)
|
||||
- ✅ 记录客户端信息(IP、User-Agent)
|
||||
- ✅ 错误请求记录详细错误信息
|
||||
|
||||
### 敏感信息保护
|
||||
- ✅ 自动过滤密码字段
|
||||
- ✅ 自动过滤 token 字段
|
||||
- ✅ 自动过滤 secret 字段
|
||||
- ✅ 自动过滤 key 字段
|
||||
|
||||
### 日志管理功能
|
||||
- ✅ 多维度查询(用户、模块、操作、状态、时间、IP)
|
||||
- ✅ 分页查询
|
||||
- ✅ 日志详情查看
|
||||
- ✅ 日志统计(总数、成功数、失败数)
|
||||
- ✅ 单条删除
|
||||
- ✅ 批量删除
|
||||
- ✅ 定期清理(按天数)
|
||||
- ✅ 导出为 Excel
|
||||
|
||||
### 性能优化
|
||||
- ✅ 日志记录在请求处理后执行
|
||||
- ✅ 不影响业务响应速度
|
||||
- ✅ 异常处理,记录失败不影响业务
|
||||
- ✅ 支持分页查询,避免一次性加载过多数据
|
||||
|
||||
## API 接口列表
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/admin/logs` | GET | 获取日志列表 |
|
||||
| `/admin/logs/{id}` | GET | 获取日志详情 |
|
||||
| `/admin/logs/statistics` | GET | 获取日志统计 |
|
||||
| `/admin/logs/export` | POST | 导出日志(Excel) |
|
||||
| `/admin/logs/{id}` | DELETE | 删除单条日志 |
|
||||
| `/admin/logs/batch-delete` | POST | 批量删除日志 |
|
||||
| `/admin/logs/clear` | POST | 清理历史日志 |
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### system_logs 表
|
||||
|
||||
已存在的表结构,包含以下字段:
|
||||
- id: 主键
|
||||
- user_id: 用户 ID
|
||||
- username: 用户名
|
||||
- module: 模块名称
|
||||
- action: 操作名称
|
||||
- method: 请求方法
|
||||
- url: 请求 URL
|
||||
- ip: 客户端 IP
|
||||
- user_agent: 用户代理
|
||||
- params: 请求参数(JSON)
|
||||
- result: 响应结果
|
||||
- status_code: HTTP 状态码
|
||||
- status: 状态(success/error)
|
||||
- error_message: 错误信息
|
||||
- execution_time: 执行时间(毫秒)
|
||||
- created_at: 创建时间
|
||||
- updated_at: 更新时间
|
||||
|
||||
## 中间件应用范围
|
||||
|
||||
### 已应用的路由
|
||||
- ✅ 所有 `/admin/*` 路由(除登录接口)
|
||||
- ✅ 认证相关(登出、刷新、个人信息、修改密码)
|
||||
- ✅ 用户管理
|
||||
- ✅ 角色管理
|
||||
- ✅ 权限管理
|
||||
- ✅ 部门管理
|
||||
- ✅ 在线用户管理
|
||||
- ✅ 系统配置管理
|
||||
- ✅ 数据字典管理
|
||||
- ✅ 任务管理
|
||||
- ✅ 城市数据管理
|
||||
- ✅ 文件上传管理
|
||||
|
||||
### 未应用的路由
|
||||
- ❌ 登录接口(`POST /admin/auth/login`)
|
||||
- ❌ 健康检查接口(`GET /up`)
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 后端使用
|
||||
|
||||
中间件会自动记录所有请求,无需手动调用:
|
||||
|
||||
```php
|
||||
// 任何经过 log.request 中间件的请求都会被自动记录
|
||||
Route::middleware(['auth.check:admin', 'log.request'])->group(function () {
|
||||
Route::apiResource('users', UserController::class);
|
||||
// 其他路由...
|
||||
});
|
||||
```
|
||||
|
||||
### 前端调用示例
|
||||
|
||||
```javascript
|
||||
// 获取日志列表
|
||||
const response = await request.get('/admin/logs', {
|
||||
params: {
|
||||
username: 'admin',
|
||||
module: 'users',
|
||||
status: 'success',
|
||||
page: 1,
|
||||
page_size: 20
|
||||
}
|
||||
})
|
||||
|
||||
// 导出日志
|
||||
await request.post('/admin/logs/export', {
|
||||
username: 'admin',
|
||||
status: 'error'
|
||||
}, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
|
||||
// 批量删除
|
||||
await request.post('/admin/logs/batch-delete', {
|
||||
ids: [1, 2, 3, 4, 5]
|
||||
})
|
||||
|
||||
// 清理历史日志
|
||||
await request.post('/admin/logs/clear', {
|
||||
days: 30
|
||||
})
|
||||
```
|
||||
|
||||
## 日志记录示例
|
||||
|
||||
### 成功请求日志
|
||||
```json
|
||||
{
|
||||
"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",
|
||||
"password": "******"
|
||||
},
|
||||
"result": null,
|
||||
"status_code": 200,
|
||||
"status": "success",
|
||||
"error_message": null,
|
||||
"execution_time": 125,
|
||||
"created_at": "2024-01-01 12:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 失败请求日志
|
||||
```json
|
||||
{
|
||||
"id": 2,
|
||||
"user_id": 1,
|
||||
"username": "admin",
|
||||
"module": "users",
|
||||
"action": "删除 users",
|
||||
"method": "DELETE",
|
||||
"url": "http://example.com/admin/users/999",
|
||||
"ip": "192.168.1.1",
|
||||
"user_agent": "Mozilla/5.0...",
|
||||
"params": {},
|
||||
"result": "{\"code\":404,\"message\":\"用户不存在\"}",
|
||||
"status_code": 404,
|
||||
"status": "error",
|
||||
"error_message": "用户不存在",
|
||||
"execution_time": 45,
|
||||
"created_at": "2024-01-01 12:01:00"
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 性能考虑
|
||||
- 日志记录在请求处理后执行,不影响响应速度
|
||||
- 大量日志会增加数据库写入压力
|
||||
- 建议定期清理历史日志
|
||||
|
||||
### 2. 数据安全
|
||||
- 敏感信息已自动过滤
|
||||
- 日志数据应妥善保管
|
||||
- 建议定期备份重要日志
|
||||
|
||||
### 3. 权限控制
|
||||
- 日志管理接口需要相应权限
|
||||
- 建议只允许管理员查看和操作日志
|
||||
|
||||
### 4. 数据库优化
|
||||
- 确保查询字段有索引
|
||||
- 使用分页查询避免加载过多数据
|
||||
- 定期清理历史日志
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
### 1. 异步队列
|
||||
考虑使用 Laravel 队列异步处理日志记录,进一步减少对响应时间的影响。
|
||||
|
||||
### 2. 日志归档
|
||||
实现日志归档功能,将历史日志移动到归档表或文件存储。
|
||||
|
||||
### 3. 日志分析
|
||||
集成日志分析工具,提供可视化仪表盘和趋势分析。
|
||||
|
||||
### 4. 定时清理
|
||||
配置 Laravel 任务调度器,自动清理指定天数前的日志:
|
||||
|
||||
```php
|
||||
// app/Console/Kernel.php
|
||||
$schedule->call(function () {
|
||||
app(LogService::class)->clearLogs(90);
|
||||
})->dailyAt('02:00');
|
||||
```
|
||||
|
||||
### 5. 日志级别
|
||||
增加日志级别(info、warning、error、critical),便于分类管理。
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能测试
|
||||
1. 测试各种请求是否被正确记录
|
||||
2. 测试敏感信息是否被正确过滤
|
||||
3. 测试日志查询和筛选功能
|
||||
4. 测试日志导出功能
|
||||
5. 测试批量删除和清理功能
|
||||
|
||||
### 性能测试
|
||||
1. 测试日志记录对响应时间的影响
|
||||
2. 测试大量日志数据的查询性能
|
||||
3. 测试并发写入的性能
|
||||
|
||||
### 边界测试
|
||||
1. 测试异常情况下的日志记录
|
||||
2. 测试超长参数的处理
|
||||
3. 测试特殊字符的处理
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增文件
|
||||
```
|
||||
app/Http/Middleware/LogRequestMiddleware.php
|
||||
app/Http/Requests/LogRequest.php
|
||||
docs/README_LOG.md
|
||||
docs/LOG_IMPLEMENTATION_SUMMARY.md
|
||||
```
|
||||
|
||||
### 修改文件
|
||||
```
|
||||
app/Http/Controllers/System/Admin/Log.php
|
||||
app/Services/System/LogService.php
|
||||
routes/admin.php
|
||||
bootstrap/app.php
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
本次日志模块优化完善实现了:
|
||||
- ✅ 全自动化的请求日志记录
|
||||
- ✅ 完善的日志管理功能
|
||||
- ✅ 敏感信息保护
|
||||
- ✅ 多维度查询和筛选
|
||||
- ✅ 数据导出功能
|
||||
- ✅ 批量操作支持
|
||||
- ✅ 完整的文档说明
|
||||
|
||||
日志模块现已完全集成到项目中,所有后台管理 API 请求都会被自动记录,管理员可以通过日志管理功能进行系统监控、审计和问题排查。
|
||||
638
docs/README_AUTH.md
Normal file
638
docs/README_AUTH.md
Normal file
@@ -0,0 +1,638 @@
|
||||
# Auth 基础模块文档
|
||||
|
||||
## 概述
|
||||
|
||||
Auth 基础模块提供了完整的后台管理系统的认证授权功能,包括用户管理、角色管理、权限管理、部门管理等功能。
|
||||
|
||||
## 控制器结构
|
||||
|
||||
```
|
||||
app/Http/Controllers/Auth/
|
||||
└── Admin/
|
||||
├── Auth.php # 认证控制器
|
||||
├── User.php # 用户管理控制器
|
||||
├── Role.php # 角色管理控制器
|
||||
├── Permission.php # 权限管理控制器
|
||||
└── Department.php # 部门管理控制器
|
||||
```
|
||||
|
||||
**命名空间**: `App\Http\Controllers\Auth\Admin`
|
||||
|
||||
**路由前缀**: `/admin`
|
||||
|
||||
**中间件**: `auth.check:admin` (后台管理认证)
|
||||
|
||||
## 技术栈
|
||||
|
||||
- PHP 8.1+
|
||||
- Laravel 11
|
||||
- JWT 认证 (tymon/jwt-auth)
|
||||
- Redis 缓存
|
||||
- Laravel Excel (maatwebsite/excel)
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### auth_users (用户表)
|
||||
- `id`: 主键
|
||||
- `username`: 用户名(唯一)
|
||||
- `password`: 密码(加密)
|
||||
- `real_name`: 真实姓名
|
||||
- `email`: 邮箱
|
||||
- `phone`: 手机号
|
||||
- `avatar`: 头像
|
||||
- `department_id`: 部门ID
|
||||
- `status`: 状态(0:禁用, 1:启用)
|
||||
- `last_login_at`: 最后登录时间
|
||||
- `last_login_ip`: 最后登录IP
|
||||
- `last_active_at`: 最后活跃时间
|
||||
- `created_at`, `updated_at`: 时间戳
|
||||
|
||||
### auth_roles (角色表)
|
||||
- `id`: 主键
|
||||
- `name`: 角色名称
|
||||
- `code`: 角色编码(唯一)
|
||||
- `description`: 角色描述
|
||||
- `sort`: 排序
|
||||
- `status`: 状态
|
||||
- `created_at`, `updated_at`: 时间戳
|
||||
|
||||
### auth_permissions (权限表)
|
||||
- `id`: 主键
|
||||
- `name`: 权限名称
|
||||
- `code`: 权限编码(唯一,格式:模块.功能.操作)
|
||||
- `type`: 类型(menu:菜单, api:接口, button:按钮)
|
||||
- `route`: 路由
|
||||
- `component`: 组件路径
|
||||
- `icon`: 图标
|
||||
- `parent_id`: 父级ID
|
||||
- `meta`: 元数据(JSON格式)
|
||||
- `sort`: 排序
|
||||
- `status`: 状态
|
||||
- `created_at`, `updated_at`: 时间戳
|
||||
|
||||
### auth_role_permission (角色权限关联表)
|
||||
- `id`: 主键
|
||||
- `role_id`: 角色ID
|
||||
- `permission_id`: 权限ID
|
||||
|
||||
### auth_user_role (用户角色关联表)
|
||||
- `id`: 主键
|
||||
- `user_id`: 用户ID
|
||||
- `role_id`: 角色ID
|
||||
|
||||
### auth_departments (部门表)
|
||||
- `id`: 主键
|
||||
- `name`: 部门名称
|
||||
- `parent_id`: 父级ID
|
||||
- `leader`: 负责人
|
||||
- `phone`: 联系电话
|
||||
- `sort`: 排序
|
||||
- `status`: 状态
|
||||
- `created_at`, `updated_at`: 时间戳
|
||||
|
||||
## API 接口文档
|
||||
|
||||
### 中间件说明
|
||||
|
||||
### AuthCheckMiddleware
|
||||
|
||||
这是一个通用的认证中间件,支持通过参数指定不同的路由守卫和权限验证。
|
||||
|
||||
#### 基本用法
|
||||
|
||||
**1. 只进行登录认证(不检查权限)**
|
||||
|
||||
```php
|
||||
// 使用 admin 守卫
|
||||
Route::middleware(['auth.check:admin'])->group(function () {
|
||||
Route::get('/users', [UserController::class, 'index']);
|
||||
});
|
||||
|
||||
// 使用 api 守卫
|
||||
Route::middleware(['auth.check:api'])->group(function () {
|
||||
Route::get('/profile', [UserController::class, 'profile']);
|
||||
});
|
||||
```
|
||||
|
||||
**2. 登录认证 + 权限验证**
|
||||
|
||||
```php
|
||||
// 验证用户是否有特定权限
|
||||
Route::middleware(['auth.check:admin,system.user.list'])->group(function () {
|
||||
Route::get('/users', [UserController::class, 'index']);
|
||||
});
|
||||
|
||||
// 验证多个权限
|
||||
Route::middleware(['auth.check:admin,system.user.create'])->group(function () {
|
||||
Route::post('/users', [UserController::class, 'store']);
|
||||
});
|
||||
```
|
||||
|
||||
**3. 单个路由应用中间件**
|
||||
|
||||
```php
|
||||
Route::middleware('auth.check:admin,system.user.delete')->delete('/users/{id}', [UserController::class, 'destroy']);
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
- `guard`: 认证守卫名称(必需),例如:`admin`、`api`
|
||||
- `permission`: 权限编码(可选),如果指定则会验证用户是否有该权限
|
||||
|
||||
#### 功能特性
|
||||
|
||||
- ✅ 支持多个路由守卫(admin、api等)
|
||||
- ✅ 支持登录状态检查
|
||||
- ✅ 支持用户状态检查(禁用用户无法访问)
|
||||
- ✅ 支持权限验证
|
||||
- ✅ 自动将用户信息注入到请求中($request->auth_user)
|
||||
- ✅ 自动更新用户最后活跃时间
|
||||
|
||||
#### 认证相关
|
||||
|
||||
#### 登录
|
||||
- **接口**: `POST /admin/auth/login`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "123456"
|
||||
}
|
||||
```
|
||||
- **返回**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "登录成功",
|
||||
"data": {
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
|
||||
"user": {...},
|
||||
"menu": {...},
|
||||
"permissions": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 登出
|
||||
- **接口**: `POST /admin/auth/logout`
|
||||
- **Header**: `Authorization: Bearer {token}`
|
||||
|
||||
#### 刷新Token
|
||||
- **接口**: `POST /admin/auth/refresh`
|
||||
- **Header**: `Authorization: Bearer {token}`
|
||||
|
||||
#### 获取当前用户信息
|
||||
- **接口**: `GET /admin/auth/me`
|
||||
- **Header**: `Authorization: Bearer {token}`
|
||||
|
||||
#### 修改密码
|
||||
- **接口**: `POST /admin/auth/change-password`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"old_password": "123456",
|
||||
"new_password": "654321"
|
||||
}
|
||||
```
|
||||
|
||||
### 用户管理
|
||||
|
||||
#### 获取用户列表
|
||||
- **接口**: `GET /admin/users`
|
||||
- **参数**:
|
||||
- `page`: 页码(默认1)
|
||||
- `page_size`: 每页数量(默认20)
|
||||
- `keyword`: 搜索关键词(用户名/真实姓名/邮箱)
|
||||
- `status`: 状态(0:禁用, 1:启用)
|
||||
- `department_id`: 部门ID
|
||||
- `role_id`: 角色ID
|
||||
- `order_by`: 排序字段(默认id)
|
||||
- `order_direction`: 排序方向(asc/desc,默认asc)
|
||||
|
||||
#### 获取用户详情
|
||||
- **接口**: `GET /admin/users/{id}`
|
||||
|
||||
#### 创建用户
|
||||
- **接口**: `POST /admin/users`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"username": "test001",
|
||||
"password": "123456",
|
||||
"real_name": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": "13800138000",
|
||||
"department_id": 1,
|
||||
"role_ids": [1, 2],
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新用户
|
||||
- **接口**: `PUT /admin/users/{id}`
|
||||
- **参数**: 同创建用户(所有字段都是可选的)
|
||||
|
||||
#### 删除用户
|
||||
- **接口**: `DELETE /admin/users/{id}`
|
||||
|
||||
#### 批量删除用户
|
||||
- **接口**: `POST /admin/users/batch-delete`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
#### 批量更新用户状态
|
||||
- **接口**: `POST /admin/users/batch-status`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3],
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 批量分配部门
|
||||
- **接口**: `POST /admin/users/batch-department`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3],
|
||||
"department_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 批量分配角色
|
||||
- **接口**: `POST /admin/users/batch-roles`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3],
|
||||
"role_ids": [1, 2]
|
||||
}
|
||||
```
|
||||
|
||||
#### 导出用户
|
||||
- **接口**: `POST /admin/users/export`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3] // 可选,不传则导出所有用户
|
||||
}
|
||||
```
|
||||
|
||||
#### 导入用户
|
||||
- **接口**: `POST /admin/users/import`
|
||||
- **参数**: `file` (multipart/form-data, xlsx/xls文件)
|
||||
- **返回**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "导入完成,成功 10 条,失败 0 条",
|
||||
"data": {
|
||||
"success_count": 10,
|
||||
"error_count": 0,
|
||||
"errors": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 下载用户导入模板
|
||||
- **接口**: `GET /admin/users/download-template`
|
||||
|
||||
### 在线用户管理
|
||||
|
||||
#### 获取在线用户数量
|
||||
- **接口**: `GET /admin/online-users/count`
|
||||
|
||||
#### 获取在线用户列表
|
||||
- **接口**: `GET /admin/online-users`
|
||||
- **参数**:
|
||||
- `limit`: 数量限制(默认100)
|
||||
|
||||
#### 获取用户的所有会话
|
||||
- **接口**: `GET /admin/online-users/{userId}/sessions`
|
||||
|
||||
#### 强制用户下线(单个会话)
|
||||
- **接口**: `POST /admin/online-users/{userId}/offline`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"token": "用户token"
|
||||
}
|
||||
```
|
||||
|
||||
#### 强制用户所有设备下线
|
||||
- **接口**: `POST /admin/online-users/{userId}/offline-all`
|
||||
|
||||
### 角色管理
|
||||
|
||||
#### 获取角色列表
|
||||
- **接口**: `GET /admin/roles`
|
||||
- **参数**:
|
||||
- `page`, `page_size`, `keyword`, `status`, `order_by`, `order_direction`
|
||||
|
||||
#### 获取所有角色(不分页)
|
||||
- **接口**: `GET /admin/roles/all`
|
||||
|
||||
#### 获取角色详情
|
||||
- **接口**: `GET /admin/roles/{id}`
|
||||
|
||||
#### 创建角色
|
||||
- **接口**: `POST /admin/roles`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"name": "编辑",
|
||||
"code": "editor",
|
||||
"description": "编辑角色",
|
||||
"sort": 1,
|
||||
"status": 1,
|
||||
"permission_ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新角色
|
||||
- **接口**: `PUT /admin/roles/{id}`
|
||||
|
||||
#### 删除角色
|
||||
- **接口**: `DELETE /admin/roles/{id}`
|
||||
|
||||
#### 批量删除角色
|
||||
- **接口**: `POST /admin/roles/batch-delete`
|
||||
|
||||
#### 批量更新角色状态
|
||||
- **接口**: `POST /admin/roles/batch-status`
|
||||
|
||||
#### 分配权限
|
||||
- **接口**: `POST /admin/roles/{id}/permissions`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"permission_ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
#### 获取角色的权限列表
|
||||
- **接口**: `GET /admin/roles/{id}/permissions`
|
||||
|
||||
#### 复制角色
|
||||
- **接口**: `POST /admin/roles/{id}/copy`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"name": "新角色名称",
|
||||
"code": "new_role_code",
|
||||
"description": "新角色描述",
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 批量复制角色
|
||||
- **接口**: `POST /admin/roles/batch-copy`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3],
|
||||
"name": "新角色名称(可选)",
|
||||
"code": "new_code(可选)"
|
||||
}
|
||||
```
|
||||
|
||||
### 权限管理
|
||||
|
||||
#### 获取权限列表
|
||||
- **接口**: `GET /admin/permissions`
|
||||
- **参数**:
|
||||
- `page`, `page_size`, `keyword`, `type`, `status`, `order_by`, `order_direction`
|
||||
|
||||
#### 获取权限树
|
||||
- **接口**: `GET /admin/permissions/tree`
|
||||
|
||||
#### 获取菜单树
|
||||
- **接口**: `GET /admin/permissions/menu`
|
||||
- **返回**: 当前登录用户的菜单树
|
||||
|
||||
#### 获取权限详情
|
||||
- **接口**: `GET /admin/permissions/{id}`
|
||||
|
||||
#### 创建权限
|
||||
- **接口**: `POST /admin/permissions`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"name": "用户列表",
|
||||
"code": "system.user.list",
|
||||
"type": "api",
|
||||
"route": "/admin/users",
|
||||
"component": "",
|
||||
"icon": "user",
|
||||
"parent_id": 0,
|
||||
"sort": 1,
|
||||
"status": 1,
|
||||
"meta": {
|
||||
"title": "用户列表",
|
||||
"keepAlive": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新权限
|
||||
- **接口**: `PUT /admin/permissions/{id}`
|
||||
|
||||
#### 删除权限
|
||||
- **接口**: `DELETE /admin/permissions/{id}`
|
||||
|
||||
#### 批量删除权限
|
||||
- **接口**: `POST /admin/permissions/batch-delete`
|
||||
|
||||
#### 批量更新权限状态
|
||||
- **接口**: `POST /admin/permissions/batch-status`
|
||||
|
||||
### 部门管理
|
||||
|
||||
#### 获取部门列表
|
||||
- **接口**: `GET /admin/departments`
|
||||
- **参数**:
|
||||
- `page`, `page_size`, `keyword`, `status`, `order_by`, `order_direction`
|
||||
|
||||
#### 获取部门树
|
||||
- **接口**: `GET /admin/departments/tree`
|
||||
|
||||
#### 获取所有部门(不分页)
|
||||
- **接口**: `GET /admin/departments/all`
|
||||
|
||||
#### 获取部门详情
|
||||
- **接口**: `GET /admin/departments/{id}`
|
||||
|
||||
#### 创建部门
|
||||
- **接口**: `POST /admin/departments`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"name": "技术部",
|
||||
"parent_id": 0,
|
||||
"leader": "张三",
|
||||
"phone": "13800138000",
|
||||
"sort": 1,
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新部门
|
||||
- **接口**: `PUT /admin/departments/{id}`
|
||||
|
||||
#### 删除部门
|
||||
- **接口**: `DELETE /admin/departments/{id}`
|
||||
|
||||
#### 批量删除部门
|
||||
- **接口**: `POST /admin/departments/batch-delete`
|
||||
|
||||
#### 批量更新部门状态
|
||||
- **接口**: `POST /admin/departments/batch-status`
|
||||
|
||||
#### 导出部门
|
||||
- **接口**: `POST /admin/departments/export`
|
||||
|
||||
#### 导入部门
|
||||
- **接口**: `POST /admin/departments/import`
|
||||
- **参数**: `file` (multipart/form-data, xlsx/xls文件)
|
||||
|
||||
#### 下载部门导入模板
|
||||
- **接口**: `GET /admin/departments/download-template`
|
||||
|
||||
## 权限设计
|
||||
|
||||
### 权限编码规则
|
||||
|
||||
权限编码采用 `模块.功能.操作` 的格式,例如:
|
||||
- `system.user.list` - 系统管理-用户-列表
|
||||
- `system.user.create` - 系统管理-用户-创建
|
||||
- `system.user.update` - 系统管理-用户-更新
|
||||
- `system.user.delete` - 系统管理-用户-删除
|
||||
|
||||
### 权限类型
|
||||
|
||||
- **menu**: 菜单类型,用于前端路由配置
|
||||
- **api**: API接口类型,用于后端权限验证
|
||||
- **button**: 按钮类型,用于前端按钮权限控制
|
||||
|
||||
## 缓存机制
|
||||
|
||||
### 用户在线状态缓存
|
||||
|
||||
- **缓存键**: `user_online:{userId}:{tokenHash}`
|
||||
- **过期时间**: 5分钟
|
||||
- **用途**: 跟踪用户在线状态、最后活跃时间、登录设备信息
|
||||
|
||||
### 权限缓存
|
||||
|
||||
- **用户权限列表**: `permission:user:{userId}:permissions` (60分钟)
|
||||
- **用户权限编码**: `permission:user:{userId}:permission_codes` (60分钟)
|
||||
- **用户菜单树**: `permission:user:{userId}:menu_tree` (60分钟)
|
||||
- **角色权限**: `permission:role:{roleId}:permissions` (60分钟)
|
||||
|
||||
### 缓存更新时机
|
||||
|
||||
- 用户登录/刷新token时,更新在线状态
|
||||
- 用户角色变化时,清除用户权限缓存
|
||||
- 角色权限变化时,清除角色和所有关联用户的权限缓存
|
||||
- 权限本身变化时,清除所有权限缓存
|
||||
|
||||
## 导入导出
|
||||
|
||||
### 用户导入模板字段
|
||||
|
||||
- 用户名*(必填)
|
||||
- 密码*(必填)
|
||||
- 真实姓名*(必填)
|
||||
- 邮箱
|
||||
- 手机号
|
||||
- 部门名称
|
||||
- 角色名称(多个用逗号分隔)
|
||||
- 备注
|
||||
|
||||
### 部门导入模板字段
|
||||
|
||||
- 部门名称*(必填)
|
||||
- 上级部门名称
|
||||
- 负责人
|
||||
- 联系电话
|
||||
- 排序
|
||||
- 备注
|
||||
|
||||
### 导出功能
|
||||
|
||||
- 支持导出全部数据
|
||||
- 支持按选中的ID导出
|
||||
- 导出文件为Excel格式
|
||||
- 下载后自动删除临时文件
|
||||
|
||||
## 初始化数据
|
||||
|
||||
运行数据库迁移和填充命令:
|
||||
|
||||
```bash
|
||||
# 执行迁移
|
||||
php artisan migrate
|
||||
|
||||
# 填充初始数据
|
||||
php artisan db:seed --class=AuthSeeder
|
||||
```
|
||||
|
||||
初始数据包括:
|
||||
- 超级管理员账号(username: admin, password: 123456)
|
||||
- 基础角色(超级管理员、管理员)
|
||||
- 完整的权限菜单
|
||||
- 默认部门
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **Swoole环境注意事项**:
|
||||
- 避免使用静态变量存储状态
|
||||
- 避免使用全局变量
|
||||
- 正确管理数据库连接
|
||||
- 使用Redis缓存时注意连接池配置
|
||||
|
||||
2. **安全注意事项**:
|
||||
- 所有密码必须加密存储
|
||||
- 使用JWT进行身份认证
|
||||
- 敏感操作需要记录日志
|
||||
- 定期清理过期的导出文件
|
||||
|
||||
3. **性能优化**:
|
||||
- 使用Redis缓存权限数据
|
||||
- 大批量操作使用队列处理
|
||||
- 分页查询避免一次性加载过多数据
|
||||
|
||||
4. **权限验证**:
|
||||
- 在中间件中验证用户权限
|
||||
- 前端根据权限控制按钮显示
|
||||
- 使用权限缓存减少数据库查询
|
||||
|
||||
## 扩展建议
|
||||
|
||||
1. **日志审计**: 添加操作日志记录
|
||||
2. **数据权限**: 实现基于部门的数据权限控制
|
||||
3. **多租户**: 支持多租户场景
|
||||
4. **SSO登录**: 支持第三方单点登录
|
||||
5. **动态权限**: 支持运行时动态添加权限
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何添加新的权限模块?
|
||||
A: 在Seed文件中添加新的权限数据,或者通过API创建新的权限节点。
|
||||
|
||||
### Q: 导入大量数据时超时怎么办?
|
||||
A: 使用队列处理导入任务,分批导入数据。
|
||||
|
||||
### Q: 如何清理权限缓存?
|
||||
A: 调用 `PermissionCacheService::clearAllPermissionCache()` 方法。
|
||||
|
||||
### Q: Swoole环境下如何热重载?
|
||||
A: 运行 `php bin/laravels reload` 命令进行平滑重启。
|
||||
|
||||
### Q: Excel文件支持哪些格式?
|
||||
A: 支持.xlsx和.xls格式文件。
|
||||
608
docs/README_LOG.md
Normal file
608
docs/README_LOG.md
Normal file
@@ -0,0 +1,608 @@
|
||||
# 系统操作日志模块文档
|
||||
|
||||
## 概述
|
||||
|
||||
系统操作日志模块用于记录后台管理系统的所有操作请求,包括用户操作、API 调用、错误信息等,方便管理员进行系统监控、审计和问题排查。
|
||||
|
||||
## 技术特性
|
||||
|
||||
- **自动记录**: 通过中间件自动记录所有请求,无需手动调用
|
||||
- **详细信息**: 记录用户信息、请求参数、响应结果、执行时间等
|
||||
- **敏感信息保护**: 自动过滤密码等敏感信息
|
||||
- **性能优化**: 不影响业务响应速度
|
||||
- **多维度查询**: 支持按用户、模块、操作、状态、时间等多维度筛选
|
||||
- **数据导出**: 支持导出日志数据为 Excel 文件
|
||||
- **批量操作**: 支持批量删除和定期清理
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### system_logs 表
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| id | bigint | 主键 ID |
|
||||
| user_id | bigint | 用户 ID |
|
||||
| username | varchar(100) | 用户名 |
|
||||
| module | varchar(50) | 模块名称 |
|
||||
| action | varchar(100) | 操作名称 |
|
||||
| method | varchar(10) | 请求方法 (GET/POST/PUT/DELETE) |
|
||||
| url | text | 请求 URL |
|
||||
| ip | varchar(45) | 客户端 IP 地址 |
|
||||
| user_agent | text | 用户代理 |
|
||||
| params | json | 请求参数 |
|
||||
| result | text | 响应结果(仅错误时记录) |
|
||||
| status_code | int | HTTP 状态码 |
|
||||
| status | varchar(20) | 状态 (success/error) |
|
||||
| error_message | text | 错误信息 |
|
||||
| execution_time | int | 执行时间(毫秒) |
|
||||
| created_at | timestamp | 创建时间 |
|
||||
| updated_at | timestamp | 更新时间 |
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 1. 中间件 (Middleware)
|
||||
|
||||
**LogRequestMiddleware**
|
||||
|
||||
位置: `app/Http/Middleware/LogRequestMiddleware.php`
|
||||
|
||||
功能:
|
||||
- 自动拦截所有经过的请求
|
||||
- 记录请求和响应信息
|
||||
- 计算请求执行时间
|
||||
- 提取用户信息和操作详情
|
||||
- 过滤敏感参数
|
||||
- 处理异常情况
|
||||
|
||||
使用方式:
|
||||
```php
|
||||
// 在路由中应用
|
||||
Route::middleware(['log.request'])->group(function () {
|
||||
// 需要记录日志的路由
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 服务层 (Service)
|
||||
|
||||
**LogService**
|
||||
|
||||
位置: `app/Services/System/LogService.php`
|
||||
|
||||
主要方法:
|
||||
- `create(array $data)`: 创建日志记录
|
||||
- `getList(array $params)`: 获取日志列表(分页)
|
||||
- `getListQuery(array $params)`: 获取日志查询构建器
|
||||
- `getById(int $id)`: 根据 ID 获取日志详情
|
||||
- `delete(int $id)`: 删除单条日志
|
||||
- `batchDelete(array $ids)`: 批量删除日志
|
||||
- `clearLogs(string $days)`: 清理指定天数前的日志
|
||||
- `getStatistics(array $params)`: 获取日志统计信息
|
||||
|
||||
### 3. 控制器 (Controller)
|
||||
|
||||
**Log Controller**
|
||||
|
||||
位置: `app/Http/Controllers/System/Admin/Log.php`
|
||||
|
||||
接口列表:
|
||||
- `GET /admin/logs`: 获取日志列表
|
||||
- `GET /admin/logs/{id}`: 获取日志详情
|
||||
- `GET /admin/logs/statistics`: 获取日志统计
|
||||
- `POST /admin/logs/export`: 导出日志
|
||||
- `DELETE /admin/logs/{id}`: 删除单条日志
|
||||
- `POST /admin/logs/batch-delete`: 批量删除日志
|
||||
- `POST /admin/logs/clear`: 清理历史日志
|
||||
|
||||
### 4. 请求验证 (Request Validation)
|
||||
|
||||
**LogRequest**
|
||||
|
||||
位置: `app/Http/Requests/LogRequest.php`
|
||||
|
||||
验证规则:
|
||||
- `user_id`: 用户 ID(可选)
|
||||
- `username`: 用户名(模糊查询,可选)
|
||||
- `module`: 模块名称(可选)
|
||||
- `action`: 操作名称(可选)
|
||||
- `status`: 状态(success/error,可选)
|
||||
- `start_date`: 开始日期(可选)
|
||||
- `end_date`: 结束日期(可选)
|
||||
- `ip`: IP 地址(可选)
|
||||
- `page`: 页码(默认 1)
|
||||
- `page_size`: 每页数量(默认 20,最大 100)
|
||||
|
||||
## API 接口文档
|
||||
|
||||
### 1. 获取日志列表
|
||||
|
||||
**接口**: `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",
|
||||
"user": {
|
||||
"id": 1,
|
||||
"name": "管理员",
|
||||
"username": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 获取日志详情
|
||||
|
||||
**接口**: `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",
|
||||
"email": "admin@example.com",
|
||||
"created_at": "2024-01-01 10:00:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 获取日志统计
|
||||
|
||||
**接口**: `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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 导出日志
|
||||
|
||||
**接口**: `POST /admin/logs/export`
|
||||
|
||||
**请求参数**: 与获取日志列表相同的查询参数
|
||||
|
||||
**响应**: Excel 文件下载
|
||||
|
||||
文件名格式: `系统操作日志_YYYYMMDDHHmmss.xlsx`
|
||||
|
||||
包含字段:
|
||||
- ID
|
||||
- 用户名
|
||||
- 模块
|
||||
- 操作
|
||||
- 请求方法
|
||||
- URL
|
||||
- IP 地址
|
||||
- 状态码
|
||||
- 状态
|
||||
- 错误信息
|
||||
- 执行时间(ms)
|
||||
- 创建时间
|
||||
|
||||
### 5. 删除单条日志
|
||||
|
||||
**接口**: `DELETE /admin/logs/{id}`
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "删除成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 批量删除日志
|
||||
|
||||
**接口**: `POST /admin/logs/batch-delete`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3, 4, 5]
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "批量删除成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 清理历史日志
|
||||
|
||||
**接口**: `POST /admin/logs/clear`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"days": 30
|
||||
}
|
||||
```
|
||||
|
||||
**说明**: 清理指定天数前的所有日志记录,默认清理 30 天前的数据。
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "清理成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 日志记录规则
|
||||
|
||||
### 1. 自动记录的请求
|
||||
|
||||
所有经过 `log.request` 中间件的请求都会被自动记录,包括:
|
||||
- 用户管理操作
|
||||
- 角色管理操作
|
||||
- 权限管理操作
|
||||
- 部门管理操作
|
||||
- 系统配置操作
|
||||
- 其他所有后台管理操作
|
||||
|
||||
### 2. 不记录的请求
|
||||
|
||||
- 登录接口 (`POST /admin/auth/login`)
|
||||
- 健康检查接口 (`GET /up`)
|
||||
- 其他明确排除的路由
|
||||
|
||||
### 3. 敏感信息过滤
|
||||
|
||||
以下字段会被自动过滤,记录为 `******`:
|
||||
- `password`
|
||||
- `password_confirmation`
|
||||
- `token`
|
||||
- `secret`
|
||||
- `key`
|
||||
|
||||
### 4. 错误日志处理
|
||||
|
||||
- 成功请求 (HTTP 状态码 < 400): `status` = `success`
|
||||
- 失败请求 (HTTP 状态码 >= 400): `status` = `error`
|
||||
- 错误时记录响应内容和错误消息
|
||||
- 同时写入 Laravel 日志文件 (`storage/logs/laravel.log`)
|
||||
|
||||
## 模块和操作名称解析
|
||||
|
||||
### 模块名称
|
||||
|
||||
从 URL 路径中解析,例如:
|
||||
- `/admin/users` → 模块: `users`
|
||||
- `/admin/roles` → 模块: `roles`
|
||||
- `/admin/configs` → 模块: `configs`
|
||||
|
||||
### 操作名称
|
||||
|
||||
根据 HTTP 方法和资源名称生成:
|
||||
- `GET /admin/users` → 操作: `查询 users`
|
||||
- `POST /admin/users` → 操作: `创建 users`
|
||||
- `PUT /admin/users/1` → 操作: `更新 users`
|
||||
- `DELETE /admin/users/1` → 操作: `删除 users`
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 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. 数据库索引
|
||||
|
||||
确保以下字段有索引:
|
||||
- `user_id`
|
||||
- `username`
|
||||
- `module`
|
||||
- `status`
|
||||
- `created_at`
|
||||
|
||||
### 3. 分页查询
|
||||
|
||||
列表查询必须使用分页,避免一次加载过多数据。
|
||||
|
||||
### 4. 异步记录
|
||||
|
||||
日志记录操作应放在请求处理后,不影响响应速度。
|
||||
|
||||
## 前端集成示例
|
||||
|
||||
### Vue3 + Ant Design Vue
|
||||
|
||||
```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. **权限控制**: 日志管理接口需要相应的权限才能访问
|
||||
2. **数据安全**: 敏感信息已自动过滤,但仍需注意日志数据的安全存储
|
||||
3. **性能影响**: 虽然日志记录不影响响应速度,但大量日志会增加数据库负载
|
||||
4. **定期备份**: 重要日志数据建议定期备份
|
||||
5. **日志分析**: 可结合 BI 工具对日志数据进行深度分析
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么某些请求没有被记录?
|
||||
|
||||
A: 检查路由是否应用了 `log.request` 中间件,或者在中间件中是否被排除了。
|
||||
|
||||
### Q2: 日志数据过多怎么办?
|
||||
|
||||
A: 使用 `clearLogs` 方法定期清理历史日志,或设置任务调度器自动清理。
|
||||
|
||||
### Q3: 如何自定义日志记录规则?
|
||||
|
||||
A: 修改 `LogRequestMiddleware` 中的 `parseModule` 和 `parseAction` 方法。
|
||||
|
||||
### Q4: 日志记录会影响性能吗?
|
||||
|
||||
A: 日志记录在请求处理后执行,不影响响应速度。但大量日志会增加数据库写入压力。
|
||||
|
||||
### Q5: 如何查看完整的请求参数?
|
||||
|
||||
A: 在日志详情接口中,`params` 字段包含了完整的请求参数(敏感信息已过滤)。
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.0.0 (2024-01-01)
|
||||
- 初始版本
|
||||
- 实现基础日志记录功能
|
||||
- 支持多维度查询和筛选
|
||||
- 支持数据导出
|
||||
- 支持批量删除和清理
|
||||
792
docs/README_SYSTEM.md
Normal file
792
docs/README_SYSTEM.md
Normal file
@@ -0,0 +1,792 @@
|
||||
# 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 (图像处理)
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### system_configs (系统配置表)
|
||||
- `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`: 请求方法
|
||||
- `url`: 请求URL
|
||||
- `ip`: IP地址
|
||||
- `user_agent`: 用户代理
|
||||
- `request_data`: 请求数据(JSON)
|
||||
- `response_data`: 响应数据(JSON)
|
||||
- `duration`: 执行时间(毫秒)
|
||||
- `status_code`: 状态码
|
||||
- `created_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
|
||||
}
|
||||
```
|
||||
|
||||
### 操作日志管理
|
||||
|
||||
#### 获取日志列表
|
||||
- **接口**: `GET /admin/logs`
|
||||
- **参数**:
|
||||
- `page`, `page_size`
|
||||
- `keyword`: 搜索关键词(用户名/模块/操作)
|
||||
- `module`: 模块
|
||||
- `action`: 操作
|
||||
- `user_id`: 用户ID
|
||||
- `start_date`: 开始日期
|
||||
- `end_date`: 结束日期
|
||||
- `order_by`, `order_direction`
|
||||
|
||||
#### 获取日志详情
|
||||
- **接口**: `GET /admin/logs/{id}`
|
||||
|
||||
#### 删除日志
|
||||
- **接口**: `DELETE /admin/logs/{id}`
|
||||
|
||||
#### 批量删除日志
|
||||
- **接口**: `POST /admin/logs/batch-delete`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
#### 清理日志
|
||||
- **接口**: `POST /admin/logs/clear`
|
||||
- **参数**:
|
||||
```json
|
||||
{
|
||||
"days": 30
|
||||
}
|
||||
```
|
||||
- **说明**: 删除指定天数之前的日志记录
|
||||
|
||||
#### 获取日志统计
|
||||
- **接口**: `GET /admin/logs/statistics`
|
||||
- **参数**:
|
||||
- `start_date`: 开始日期
|
||||
- `end_date`: 结束日期
|
||||
- **返回**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"total_count": 1000,
|
||||
"module_stats": [
|
||||
{
|
||||
"module": "user",
|
||||
"count": 500
|
||||
}
|
||||
],
|
||||
"user_stats": [
|
||||
{
|
||||
"user_id": 1,
|
||||
"username": "admin",
|
||||
"count": 800
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 数据字典管理
|
||||
|
||||
#### 获取字典列表
|
||||
- **接口**: `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}`
|
||||
- **过期时间**: 60分钟
|
||||
- **更新时机**: 字典数据增删改时自动清除
|
||||
|
||||
## 服务层说明
|
||||
|
||||
### ConfigService
|
||||
|
||||
提供系统配置的CRUD操作和缓存管理。
|
||||
|
||||
**主要方法**:
|
||||
- `getList()`: 获取配置列表
|
||||
- `getByGroup()`: 按分组获取配置
|
||||
- `getConfigValue()`: 获取配置值
|
||||
- `create()`: 创建配置
|
||||
- `update()`: 更新配置
|
||||
- `delete()`: 删除配置
|
||||
|
||||
### LogService
|
||||
|
||||
提供操作日志的记录、查询和清理功能。
|
||||
|
||||
**主要方法**:
|
||||
- `getList()`: 获取日志列表
|
||||
- `getStatistics()`: 获取统计数据
|
||||
- `clearLogs()`: 清理过期日志
|
||||
- `record()`: 记录日志(由中间件自动调用)
|
||||
|
||||
### DictionaryService
|
||||
|
||||
提供数据字典和字典项的管理功能。
|
||||
|
||||
**主要方法**:
|
||||
- `getList()`: 获取字典列表
|
||||
- `getItemsByCode()`: 按编码获取字典项
|
||||
- `create()`: 创建字典
|
||||
- `createItem()`: 创建字典项
|
||||
- `update()`: 更新字典
|
||||
- `updateItem()`: 更新字典项
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
初始数据包括:
|
||||
- 基础系统配置
|
||||
- 常用数据字典
|
||||
- 全国省市区数据
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **Swoole环境注意事项**:
|
||||
- 文件上传时注意临时文件清理
|
||||
- 使用Redis缓存避免内存泄漏
|
||||
- 图片压缩使用协程安全的方式
|
||||
|
||||
2. **安全注意事项**:
|
||||
- 文件上传必须验证文件类型和大小
|
||||
- 敏感操作必须记录日志
|
||||
- 配置数据不要存储密码等敏感信息
|
||||
|
||||
3. **性能优化**:
|
||||
- 城市数据使用Redis缓存
|
||||
- 大量日志数据定期清理
|
||||
- 图片上传时进行压缩处理
|
||||
|
||||
4. **文件上传**:
|
||||
- 限制文件上传大小
|
||||
- 验证文件MIME类型
|
||||
- 定期清理临时文件
|
||||
|
||||
## 扩展建议
|
||||
|
||||
1. **日志告警**: 添加日志异常告警功能
|
||||
2. **配置加密**: 敏感配置数据加密存储
|
||||
3. **多语言**: 支持配置数据的多语言
|
||||
4. **任务监控**: 添加任务执行监控和通知
|
||||
5. **CDN集成**: 文件上传支持CDN分发
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何清除城市数据缓存?
|
||||
A: 调用 `CityService::clearCache()` 方法或运行 `php artisan cache:forget city:tree`。
|
||||
|
||||
### Q: 图片上传后如何压缩?
|
||||
A: 上传时设置 `compress=true` 和 `quality` 参数,系统会自动压缩。
|
||||
|
||||
### Q: 如何配置定时任务?
|
||||
A: 在Admin后台创建任务,设置Cron表达式,系统会自动调度执行。
|
||||
|
||||
### Q: 数据字典如何使用?
|
||||
A: 通过Public API获取字典数据,前端根据数据渲染下拉框等组件。
|
||||
|
||||
### Q: 日志数据过多如何处理?
|
||||
A: 定期使用 `/admin/logs/clear` 接口清理过期日志,或在后台设置自动清理任务。
|
||||
684
docs/README_WEBSOCKET.md
Normal file
684
docs/README_WEBSOCKET.md
Normal file
@@ -0,0 +1,684 @@
|
||||
# WebSocket 功能文档
|
||||
|
||||
## 概述
|
||||
|
||||
本项目基于 Laravel-S 和 Swoole 实现了完整的 WebSocket 功能,支持实时通信、消息推送、广播等功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 实时双向通信
|
||||
- ✅ 用户连接管理
|
||||
- ✅ 点对点消息发送
|
||||
- ✅ 群发消息/广播
|
||||
- ✅ 频道订阅/取消订阅
|
||||
- ✅ 心跳机制
|
||||
- ✅ 自动重连
|
||||
- ✅ 在线状态管理
|
||||
- ✅ 系统通知推送
|
||||
- ✅ 数据更新推送
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 后端组件
|
||||
|
||||
#### 1. WebSocketHandler (`app/Services/WebSocket/WebSocketHandler.php`)
|
||||
|
||||
WebSocket 处理器,实现了 Swoole 的 `WebSocketHandlerInterface` 接口。
|
||||
|
||||
**主要方法:**
|
||||
- `onOpen()`: 处理连接建立事件
|
||||
- `onMessage()`: 处理消息接收事件
|
||||
- `onClose()`: 处理连接关闭事件
|
||||
|
||||
**支持的消息类型:**
|
||||
- `ping/pong`: 心跳检测
|
||||
- `heartbeat`: 心跳确认
|
||||
- `chat`: 私聊消息
|
||||
- `broadcast`: 广播消息
|
||||
- `subscribe/unsubscribe`: 频道订阅/取消订阅
|
||||
|
||||
#### 2. WebSocketService (`app/Services/WebSocket/WebSocketService.php`)
|
||||
|
||||
WebSocket 服务类,提供便捷的 WebSocket 操作方法。
|
||||
|
||||
**主要方法:**
|
||||
- `sendToUser($userId, $data)`: 发送消息给指定用户
|
||||
- `sendToUsers($userIds, $data)`: 发送消息给多个用户
|
||||
- `broadcast($data, $excludeUserId)`: 广播消息给所有用户
|
||||
- `sendToChannel($channel, $data)`: 发送消息给指定频道
|
||||
- `getOnlineUserCount()`: 获取在线用户数
|
||||
- `isUserOnline($userId)`: 检查用户是否在线
|
||||
- `sendSystemNotification()`: 发送系统通知
|
||||
- `pushDataUpdate()`: 推送数据更新
|
||||
|
||||
#### 3. WebSocketController (`app/Http/Controllers/System/WebSocket.php`)
|
||||
|
||||
WebSocket API 控制器,提供 HTTP 接口用于管理 WebSocket 连接。
|
||||
|
||||
### 前端组件
|
||||
|
||||
#### WebSocketClient (`resources/admin/src/utils/websocket.js`)
|
||||
|
||||
WebSocket 客户端封装类。
|
||||
|
||||
**功能:**
|
||||
- 自动连接和重连
|
||||
- 心跳机制
|
||||
- 消息类型路由
|
||||
- 事件监听
|
||||
- 连接状态管理
|
||||
|
||||
## 配置说明
|
||||
|
||||
### Laravel-S 配置 (`config/laravels.php`)
|
||||
|
||||
```php
|
||||
'websocket' => [
|
||||
'enable' => env('LARAVELS_WEBSOCKET', true),
|
||||
'handler' => \App\Services\WebSocket\WebSocketHandler::class,
|
||||
],
|
||||
|
||||
'swoole_tables' => [
|
||||
'wsTable' => [
|
||||
'size' => 102400,
|
||||
'column' => [
|
||||
['name' => 'value', 'type' => \Swoole\Table::TYPE_STRING, 'size' => 1024],
|
||||
['name' => 'expiry', 'type' => \Swoole\Table::TYPE_INT, 'size' => 4],
|
||||
],
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
在 `.env` 文件中添加:
|
||||
|
||||
```env
|
||||
LARAVELS_WEBSOCKET=true
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 1. 获取在线用户数
|
||||
|
||||
```
|
||||
GET /admin/websocket/online-count
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"online_count": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 获取在线用户列表
|
||||
|
||||
```
|
||||
GET /admin/websocket/online-users
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"user_ids": [1, 2, 3, 4, 5],
|
||||
"count": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 检查用户在线状态
|
||||
|
||||
```
|
||||
POST /admin/websocket/check-online
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"user_id": 1,
|
||||
"is_online": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 发送消息给指定用户
|
||||
|
||||
```
|
||||
POST /admin/websocket/send-to-user
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_id": 1,
|
||||
"type": "notification",
|
||||
"data": {
|
||||
"title": "新消息",
|
||||
"message": "您有一条新消息"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 发送消息给多个用户
|
||||
|
||||
```
|
||||
POST /admin/websocket/send-to-users
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_ids": [1, 2, 3],
|
||||
"type": "notification",
|
||||
"data": {
|
||||
"title": "系统通知",
|
||||
"message": "系统将在今晚进行维护"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 广播消息
|
||||
|
||||
```
|
||||
POST /admin/websocket/broadcast
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"type": "notification",
|
||||
"data": {
|
||||
"title": "公告",
|
||||
"message": "欢迎使用新版本"
|
||||
},
|
||||
"exclude_user_id": 1 // 可选:排除某个用户
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 发送消息到频道
|
||||
|
||||
```
|
||||
POST /admin/websocket/send-to-channel
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"channel": "orders",
|
||||
"type": "data_update",
|
||||
"data": {
|
||||
"order_id": 123,
|
||||
"status": "paid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. 发送系统通知
|
||||
|
||||
```
|
||||
POST /admin/websocket/send-notification
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"title": "系统维护",
|
||||
"message": "系统将于今晚 23:00-24:00 进行维护",
|
||||
"type": "warning",
|
||||
"extra_data": {
|
||||
"start_time": "23:00",
|
||||
"end_time": "24:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9. 发送通知给指定用户
|
||||
|
||||
```
|
||||
POST /admin/websocket/send-notification-to-users
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_ids": [1, 2, 3],
|
||||
"title": "订单更新",
|
||||
"message": "您的订单已发货",
|
||||
"type": "success"
|
||||
}
|
||||
```
|
||||
|
||||
### 10. 推送数据更新
|
||||
|
||||
```
|
||||
POST /admin/websocket/push-data-update
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_ids": [1, 2, 3],
|
||||
"resource_type": "order",
|
||||
"action": "update",
|
||||
"data": {
|
||||
"id": 123,
|
||||
"status": "shipped"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 11. 推送数据更新到频道
|
||||
|
||||
```
|
||||
POST /admin/websocket/push-data-update-channel
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"channel": "orders",
|
||||
"resource_type": "order",
|
||||
"action": "create",
|
||||
"data": {
|
||||
"id": 124,
|
||||
"customer": "张三",
|
||||
"amount": 100.00
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 12. 断开用户连接
|
||||
|
||||
```
|
||||
POST /admin/websocket/disconnect-user
|
||||
```
|
||||
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"user_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
## 前端使用示例
|
||||
|
||||
### 1. 基本连接
|
||||
|
||||
```javascript
|
||||
import { getWebSocket, closeWebSocket } from '@/utils/websocket'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 连接 WebSocket
|
||||
const ws = getWebSocket(userStore.userInfo.id, userStore.token, {
|
||||
onOpen: (event) => {
|
||||
console.log('WebSocket 已连接')
|
||||
},
|
||||
onMessage: (message) => {
|
||||
console.log('收到消息:', message)
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('WebSocket 错误:', error)
|
||||
},
|
||||
onClose: (event) => {
|
||||
console.log('WebSocket 已关闭')
|
||||
}
|
||||
})
|
||||
|
||||
// 连接
|
||||
ws.connect()
|
||||
```
|
||||
|
||||
### 2. 监听特定消息类型
|
||||
|
||||
```javascript
|
||||
// 监听通知消息
|
||||
ws.on('notification', (data) => {
|
||||
message.success(data.title, data.message)
|
||||
})
|
||||
|
||||
// 监听数据更新
|
||||
ws.on('data_update', (data) => {
|
||||
console.log('数据更新:', data.resource_type, data.action)
|
||||
// 刷新数据
|
||||
loadData()
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 发送消息
|
||||
|
||||
```javascript
|
||||
// 发送心跳
|
||||
ws.send('heartbeat', { timestamp: Date.now() })
|
||||
|
||||
// 发送私聊消息
|
||||
ws.send('chat', {
|
||||
to_user_id: 2,
|
||||
content: '你好,这是一条私聊消息'
|
||||
})
|
||||
|
||||
// 订阅频道
|
||||
ws.send('subscribe', { channel: 'orders' })
|
||||
|
||||
// 取消订阅
|
||||
ws.send('unsubscribe', { channel: 'orders' })
|
||||
```
|
||||
|
||||
### 4. 发送广播消息
|
||||
|
||||
```javascript
|
||||
ws.send('broadcast', {
|
||||
message: '这是一条广播消息'
|
||||
})
|
||||
```
|
||||
|
||||
### 5. 断开连接
|
||||
|
||||
```javascript
|
||||
// 断开连接
|
||||
ws.disconnect()
|
||||
|
||||
// 或使用全局方法
|
||||
closeWebSocket()
|
||||
```
|
||||
|
||||
### 6. 在 Vue 组件中使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<a-button @click="connectWebSocket">连接 WebSocket</a-button>
|
||||
<a-button @click="disconnectWebSocket">断开连接</a-button>
|
||||
<a-button @click="sendMessage">发送消息</a-button>
|
||||
<div>连接状态: {{ connectionStatus }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { getWebSocket } from '@/utils/websocket'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const ws = ref(null)
|
||||
const connectionStatus = ref('未连接')
|
||||
|
||||
const connectWebSocket = () => {
|
||||
ws.value = getWebSocket(userStore.userInfo.id, userStore.token, {
|
||||
onOpen: () => {
|
||||
connectionStatus.value = '已连接'
|
||||
},
|
||||
onMessage: (message) => {
|
||||
handleMessage(message)
|
||||
},
|
||||
onClose: () => {
|
||||
connectionStatus.value = '已断开'
|
||||
}
|
||||
})
|
||||
|
||||
ws.value.connect()
|
||||
}
|
||||
|
||||
const disconnectWebSocket = () => {
|
||||
if (ws.value) {
|
||||
ws.value.disconnect()
|
||||
connectionStatus.value = '已断开'
|
||||
}
|
||||
}
|
||||
|
||||
const sendMessage = () => {
|
||||
if (ws.value && ws.value.isConnected) {
|
||||
ws.value.send('chat', {
|
||||
to_user_id: 2,
|
||||
content: '测试消息'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleMessage = (message) => {
|
||||
switch (message.type) {
|
||||
case 'notification':
|
||||
message.success(message.data.title, message.data.message)
|
||||
break
|
||||
case 'data_update':
|
||||
// 处理数据更新
|
||||
break
|
||||
case 'chat':
|
||||
// 处理聊天消息
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
connectWebSocket()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
disconnectWebSocket()
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 消息格式
|
||||
|
||||
### 服务端发送的消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "notification",
|
||||
"data": {
|
||||
"title": "标题",
|
||||
"message": "内容",
|
||||
"type": "info",
|
||||
"timestamp": 1641234567
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 客户端发送的消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat",
|
||||
"data": {
|
||||
"to_user_id": 2,
|
||||
"content": "消息内容"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 启动和停止
|
||||
|
||||
### 启动 Laravel-S 服务
|
||||
|
||||
```bash
|
||||
php bin/laravels start
|
||||
```
|
||||
|
||||
### 停止 Laravel-S 服务
|
||||
|
||||
```bash
|
||||
php bin/laravels stop
|
||||
```
|
||||
|
||||
### 重启 Laravel-S 服务
|
||||
|
||||
```bash
|
||||
php bin/laravels restart
|
||||
```
|
||||
|
||||
### 重载 Laravel-S 服务(平滑重启)
|
||||
|
||||
```bash
|
||||
php bin/laravels reload
|
||||
```
|
||||
|
||||
### 查看服务状态
|
||||
|
||||
```bash
|
||||
php bin/laravels status
|
||||
```
|
||||
|
||||
## WebSocket 连接地址
|
||||
|
||||
### 开发环境
|
||||
|
||||
```
|
||||
ws://localhost:5200/ws?user_id={user_id}&token={token}
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
|
||||
```
|
||||
wss://yourdomain.com/ws?user_id={user_id}&token={token}
|
||||
```
|
||||
|
||||
## Nginx 配置示例
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
root /path/to/your/project/public;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# WebSocket 代理配置
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:5200;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 1. 实时通知
|
||||
|
||||
```php
|
||||
// 发送系统通知
|
||||
$webSocketService->sendSystemNotification(
|
||||
'系统维护',
|
||||
'系统将于今晚进行维护',
|
||||
'warning'
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 订单状态更新
|
||||
|
||||
```php
|
||||
// 推送订单状态更新给相关人员
|
||||
$webSocketService->pushDataUpdate(
|
||||
[$order->user_id],
|
||||
'order',
|
||||
'update',
|
||||
[
|
||||
'id' => $order->id,
|
||||
'status' => $order->status,
|
||||
'updated_at' => $order->updated_at
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
### 3. 实时聊天
|
||||
|
||||
```javascript
|
||||
// 发送私聊消息
|
||||
ws.send('chat', {
|
||||
to_user_id: 2,
|
||||
content: '你好'
|
||||
})
|
||||
```
|
||||
|
||||
### 4. 数据监控
|
||||
|
||||
```php
|
||||
// 推送系统监控数据到特定频道
|
||||
$webSocketService->sendToChannel('system_monitor', 'monitor', [
|
||||
'cpu_usage' => 75,
|
||||
'memory_usage' => 80,
|
||||
'disk_usage' => 60
|
||||
]);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **连接认证**: WebSocket 连接时需要提供 `user_id` 和 `token` 参数
|
||||
2. **心跳机制**: 客户端默认每 30 秒发送一次心跳
|
||||
3. **自动重连**: 连接断开后会自动尝试重连,最多重试 5 次
|
||||
4. **并发限制**: Swoole Table 最多支持 102,400 个连接
|
||||
5. **内存管理**: 注意内存泄漏问题,定期重启服务
|
||||
6. **安全性**: 生产环境建议使用 WSS (WebSocket Secure)
|
||||
7. **日志监控**: 查看日志文件 `storage/logs/swoole-YYYY-MM.log`
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 1. 无法连接 WebSocket
|
||||
|
||||
- 检查 Laravel-S 服务是否启动
|
||||
- 检查端口 5200 是否被占用
|
||||
- 检查防火墙设置
|
||||
- 查看日志文件
|
||||
|
||||
### 2. 连接频繁断开
|
||||
|
||||
- 检查网络稳定性
|
||||
- 调整心跳间隔
|
||||
- 检查服务器资源使用情况
|
||||
|
||||
### 3. 消息发送失败
|
||||
|
||||
- 检查用户是否在线
|
||||
- 检查消息格式是否正确
|
||||
- 查看错误日志
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Laravel-S 文档](https://github.com/hhxsv5/laravel-s)
|
||||
- [Swoole 文档](https://www.swoole.com/)
|
||||
- [WebSocket API](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket)
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2024-02-08
|
||||
|
||||
- ✅ 初始版本发布
|
||||
- ✅ 实现基础 WebSocket 功能
|
||||
- ✅ 实现消息推送功能
|
||||
- ✅ 实现频道订阅功能
|
||||
- ✅ 实现前端客户端封装
|
||||
- ✅ 实现管理 API 接口
|
||||
Reference in New Issue
Block a user