Bladeren bron

完善ThirdParty模块:添加事件系统、监听器、后台控制器和路由配置

- 创建API调用事件、服务状态变更事件、配额告警事件、监控告警事件
- 实现对应的事件监听器:日志记录、状态监控、配额管理、告警通知
- 完善后台管理控制器:服务、凭证、日志、配额、监控管理
- 配置完整的后台路由系统,支持CRUD和扩展功能
- 修正模型字段名不一致问题,统一使用数据库表字段名
- 更新服务提供者,注册事件监听器和路由
notfff 7 maanden geleden
bovenliggende
commit
d182c2a47a

+ 275 - 0
app/Module/ThirdParty/AdminControllers/ThirdPartyCredentialController.php

@@ -0,0 +1,275 @@
+<?php
+
+namespace App\Module\ThirdParty\AdminControllers;
+
+use UCore\DcatAdmin\AdminController;
+use App\Module\ThirdParty\Models\ThirdPartyCredential;
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Repositorys\ThirdPartyCredentialRepository;
+use App\Module\ThirdParty\Enums\AUTH_TYPE;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+
+/**
+ * 第三方服务认证凭证管理控制器
+ * 
+ * 路由: /admin/thirdparty/credentials
+ */
+class ThirdPartyCredentialController extends AdminController
+{
+    /**
+     * 页面标题
+     *
+     * @var string
+     */
+    protected $title = '认证凭证管理';
+
+    /**
+     * 数据仓库
+     *
+     * @return string
+     */
+    protected function repository()
+    {
+        return ThirdPartyCredentialRepository::class;
+    }
+
+    /**
+     * 列表页面
+     *
+     * @return Grid
+     */
+    protected function grid(): Grid
+    {
+        return Grid::make(new ThirdPartyCredentialRepository(), function (Grid $grid) {
+            // 基础设置
+            $grid->column('id', 'ID')->sortable();
+            
+            // 关联服务
+            $grid->column('service.name', '所属服务')->sortable();
+            $grid->column('service.code', '服务代码');
+            
+            $grid->column('name', '凭证名称')->sortable();
+            
+            // 认证类型
+            $grid->column('type', '认证类型')->display(function ($type) {
+                $authType = AUTH_TYPE::tryFrom($type);
+                if ($authType) {
+                    $label = $authType->getLabel();
+                    $level = $authType->getSecurityLevel();
+                    $color = $authType->getSecurityLevelColor();
+                    return "<span class='badge badge-{$color}' title='安全级别: {$level}'>{$label}</span>";
+                }
+                return $type;
+            });
+
+            // 环境
+            $grid->column('environment', '环境')->display(function ($env) {
+                $colors = [
+                    'production' => 'danger',
+                    'staging' => 'warning', 
+                    'development' => 'info',
+                    'testing' => 'secondary',
+                ];
+                $color = $colors[$env] ?? 'secondary';
+                $labels = [
+                    'production' => '生产',
+                    'staging' => '预发布',
+                    'development' => '开发',
+                    'testing' => '测试',
+                ];
+                $label = $labels[$env] ?? $env;
+                return "<span class='badge badge-{$color}'>{$label}</span>";
+            });
+
+            // 状态
+            $grid->column('is_active', '状态')->display(function ($active) {
+                return $active ? 
+                    '<span class="badge badge-success"><i class="fa fa-check"></i> 激活</span>' : 
+                    '<span class="badge badge-secondary"><i class="fa fa-times"></i> 未激活</span>';
+            });
+
+            // 过期时间
+            $grid->column('expires_at', '过期时间')->display(function ($time) {
+                if (!$time) {
+                    return '<span class="badge badge-success">永不过期</span>';
+                }
+                
+                $expireTime = \Carbon\Carbon::parse($time);
+                if ($expireTime->isPast()) {
+                    return '<span class="badge badge-danger">已过期</span>';
+                } elseif ($expireTime->diffInDays() <= 7) {
+                    return '<span class="badge badge-warning">即将过期</span>';
+                } else {
+                    return $expireTime->format('Y-m-d H:i:s');
+                }
+            });
+
+            // 使用统计
+            $grid->column('usage_count', '使用次数')->sortable();
+            $grid->column('last_used_at', '最后使用')->display(function ($time) {
+                return $time ? \Carbon\Carbon::parse($time)->diffForHumans() : '未使用';
+            });
+
+            // 创建时间
+            $grid->column('created_at', '创建时间')->sortable();
+
+            // 过滤器
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->equal('service_id', '所属服务')->select(
+                    ThirdPartyService::pluck('name', 'id')->toArray()
+                );
+                $filter->equal('type', '认证类型')->select(AUTH_TYPE::getOptions());
+                $filter->equal('environment', '环境')->select([
+                    'production' => '生产',
+                    'staging' => '预发布',
+                    'development' => '开发',
+                    'testing' => '测试',
+                ]);
+                $filter->equal('is_active', '状态')->select([
+                    1 => '激活',
+                    0 => '未激活',
+                ]);
+                $filter->like('name', '凭证名称');
+            });
+
+            // 批量操作
+            $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
+                $batch->add('批量激活', new \App\Module\ThirdParty\AdminActions\BatchActivateCredentialAction());
+                $batch->add('批量停用', new \App\Module\ThirdParty\AdminActions\BatchDeactivateCredentialAction());
+            });
+
+            // 工具栏
+            $grid->tools(function (Grid\Tools $tools) {
+                $tools->append('<a href="/admin/thirdparty/credentials/expired" class="btn btn-sm btn-warning"><i class="fa fa-exclamation-triangle"></i> 过期凭证</a>');
+                $tools->append('<a href="/admin/thirdparty/credentials/test" class="btn btn-sm btn-info"><i class="fa fa-flask"></i> 测试凭证</a>');
+            });
+
+            // 行操作
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                if ($actions->row->is_active) {
+                    $actions->append('<a href="/admin/thirdparty/credentials/' . $actions->getKey() . '/test" class="btn btn-xs btn-warning"><i class="fa fa-flask"></i> 测试</a>');
+                }
+                
+                $actions->append('<a href="/admin/thirdparty/credentials/' . $actions->getKey() . '/usage" class="btn btn-xs btn-info"><i class="fa fa-chart-line"></i> 使用统计</a>');
+            });
+
+            // 默认排序
+            $grid->model()->orderBy('service_id')->orderBy('environment')->orderBy('name');
+        });
+    }
+
+    /**
+     * 详情页面
+     *
+     * @return Show
+     */
+    protected function detail($id): Show
+    {
+        return Show::make($id, new ThirdPartyCredentialRepository(), function (Show $show) {
+            $show->field('id', 'ID');
+            
+            $show->field('service.name', '所属服务');
+            $show->field('service.code', '服务代码');
+            
+            $show->field('name', '凭证名称');
+            
+            $show->field('type', '认证类型')->as(function ($type) {
+                $authType = AUTH_TYPE::tryFrom($type);
+                return $authType ? $authType->getLabel() : $type;
+            });
+
+            $show->field('environment', '环境')->as(function ($env) {
+                $labels = [
+                    'production' => '生产环境',
+                    'staging' => '预发布环境',
+                    'development' => '开发环境',
+                    'testing' => '测试环境',
+                ];
+                return $labels[$env] ?? $env;
+            });
+
+            $show->field('is_active', '状态')->as(function ($active) {
+                return $active ? '激活' : '未激活';
+            });
+
+            $show->field('expires_at', '过期时间')->as(function ($time) {
+                if (!$time) {
+                    return '永不过期';
+                }
+                
+                $expireTime = \Carbon\Carbon::parse($time);
+                $status = $expireTime->isPast() ? ' (已过期)' : 
+                         ($expireTime->diffInDays() <= 7 ? ' (即将过期)' : '');
+                
+                return $expireTime->format('Y-m-d H:i:s') . $status;
+            });
+
+            $show->field('usage_count', '使用次数');
+            $show->field('last_used_at', '最后使用时间');
+
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            // 凭证信息(脱敏显示)
+            $show->field('credentials', '凭证信息')->json();
+        });
+    }
+
+    /**
+     * 表单页面
+     *
+     * @return Form
+     */
+    protected function form(): Form
+    {
+        return Form::make(new ThirdPartyCredentialRepository(), function (Form $form) {
+            $form->display('id', 'ID');
+
+            $form->select('service_id', '所属服务')
+                ->options(ThirdPartyService::pluck('name', 'id')->toArray())
+                ->required()
+                ->help('选择要配置凭证的第三方服务');
+
+            $form->text('name', '凭证名称')->required()->help('凭证的显示名称');
+            
+            $form->select('type', '认证类型')
+                ->options(AUTH_TYPE::getOptions())
+                ->required()
+                ->help('选择认证方式');
+
+            $form->select('environment', '环境')
+                ->options([
+                    'production' => '生产环境',
+                    'staging' => '预发布环境', 
+                    'development' => '开发环境',
+                    'testing' => '测试环境',
+                ])
+                ->default('production')
+                ->required();
+
+            $form->switch('is_active', '是否激活')->default(1);
+
+            $form->datetime('expires_at', '过期时间')->help('留空表示永不过期');
+
+            // 凭证信息(根据认证类型动态显示)
+            $form->json('credentials', '凭证信息')
+                ->required()
+                ->help('根据认证类型填写相应的凭证信息,如API Key、Secret等');
+
+            $form->display('usage_count', '使用次数');
+            $form->display('last_used_at', '最后使用时间');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            // 保存前处理
+            $form->saving(function (Form $form) {
+                // 加密敏感信息
+                if ($form->credentials) {
+                    $form->credentials = encrypt($form->credentials);
+                }
+            });
+        });
+    }
+}

+ 259 - 0
app/Module/ThirdParty/AdminControllers/ThirdPartyLogController.php

@@ -0,0 +1,259 @@
+<?php
+
+namespace App\Module\ThirdParty\AdminControllers;
+
+use UCore\DcatAdmin\AdminController;
+use App\Module\ThirdParty\Models\ThirdPartyLog;
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Repositorys\ThirdPartyLogRepository;
+use App\Module\ThirdParty\Enums\LOG_LEVEL;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+
+/**
+ * 第三方服务调用日志管理控制器
+ * 
+ * 路由: /admin/thirdparty/logs
+ */
+class ThirdPartyLogController extends AdminController
+{
+    /**
+     * 页面标题
+     *
+     * @var string
+     */
+    protected $title = '调用日志管理';
+
+    /**
+     * 数据仓库
+     *
+     * @return string
+     */
+    protected function repository()
+    {
+        return ThirdPartyLogRepository::class;
+    }
+
+    /**
+     * 列表页面
+     *
+     * @return Grid
+     */
+    protected function grid(): Grid
+    {
+        return Grid::make(new ThirdPartyLogRepository(), function (Grid $grid) {
+            // 基础设置
+            $grid->column('id', 'ID')->sortable();
+            
+            // 关联服务
+            $grid->column('service.name', '服务名称')->sortable();
+            $grid->column('service.code', '服务代码');
+            
+            $grid->column('request_id', '请求ID')->copyable();
+            
+            // HTTP方法
+            $grid->column('method', '方法')->display(function ($method) {
+                $colors = [
+                    'GET' => 'success',
+                    'POST' => 'primary',
+                    'PUT' => 'warning',
+                    'DELETE' => 'danger',
+                    'PATCH' => 'info',
+                ];
+                $color = $colors[$method] ?? 'secondary';
+                return "<span class='badge badge-{$color}'>{$method}</span>";
+            });
+
+            // URL(截断显示)
+            $grid->column('url', 'URL')->display(function ($url) {
+                $shortUrl = strlen($url) > 50 ? substr($url, 0, 50) . '...' : $url;
+                return "<span title='{$url}'>{$shortUrl}</span>";
+            });
+
+            // 响应状态码
+            $grid->column('response_status', '状态码')->display(function ($status) {
+                if (!$status) {
+                    return '<span class="badge badge-secondary">-</span>';
+                }
+                
+                $color = 'secondary';
+                if ($status >= 200 && $status < 300) {
+                    $color = 'success';
+                } elseif ($status >= 300 && $status < 400) {
+                    $color = 'info';
+                } elseif ($status >= 400 && $status < 500) {
+                    $color = 'warning';
+                } elseif ($status >= 500) {
+                    $color = 'danger';
+                }
+                
+                return "<span class='badge badge-{$color}'>{$status}</span>";
+            });
+
+            // 响应时间
+            $grid->column('response_time', '响应时间')->display(function ($time) {
+                if (!$time) {
+                    return '-';
+                }
+                
+                $color = 'success';
+                if ($time > 5000) {
+                    $color = 'danger';
+                } elseif ($time > 2000) {
+                    $color = 'warning';
+                } elseif ($time > 1000) {
+                    $color = 'info';
+                }
+                
+                return "<span class='badge badge-{$color}'>{$time}ms</span>";
+            })->sortable();
+
+            // 日志级别
+            $grid->column('level', '级别')->display(function ($level) {
+                $logLevel = LOG_LEVEL::tryFrom($level);
+                if ($logLevel) {
+                    $color = $logLevel->getColor();
+                    $icon = $logLevel->getIcon();
+                    $label = $logLevel->getLabel();
+                    return "<span class='badge badge-{$color}'><i class='{$icon}'></i> {$label}</span>";
+                }
+                return $level;
+            });
+
+            // 错误信息(截断显示)
+            $grid->column('error_message', '错误信息')->display(function ($error) {
+                if (!$error) {
+                    return '-';
+                }
+                $shortError = strlen($error) > 30 ? substr($error, 0, 30) . '...' : $error;
+                return "<span class='text-danger' title='{$error}'>{$shortError}</span>";
+            });
+
+            // 用户信息
+            $grid->column('user_id', '用户ID');
+            $grid->column('ip_address', 'IP地址');
+
+            // 创建时间
+            $grid->column('created_at', '调用时间')->sortable();
+
+            // 过滤器
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->equal('service_id', '服务')->select(
+                    ThirdPartyService::pluck('name', 'id')->toArray()
+                );
+                $filter->equal('method', 'HTTP方法')->select([
+                    'GET' => 'GET',
+                    'POST' => 'POST',
+                    'PUT' => 'PUT',
+                    'DELETE' => 'DELETE',
+                    'PATCH' => 'PATCH',
+                ]);
+                $filter->equal('level', '日志级别')->select(LOG_LEVEL::getOptions());
+                $filter->between('response_status', '状态码');
+                $filter->between('response_time', '响应时间(ms)');
+                $filter->like('request_id', '请求ID');
+                $filter->like('url', 'URL');
+                $filter->like('error_message', '错误信息');
+                $filter->equal('user_id', '用户ID');
+                $filter->like('ip_address', 'IP地址');
+                $filter->between('created_at', '调用时间')->datetime();
+            });
+
+            // 批量操作
+            $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
+                $batch->add('批量删除', new \App\Module\ThirdParty\AdminActions\BatchDeleteLogAction());
+            });
+
+            // 工具栏
+            $grid->tools(function (Grid\Tools $tools) {
+                $tools->append('<a href="/admin/thirdparty/logs/stats" class="btn btn-sm btn-info"><i class="fa fa-chart-bar"></i> 统计分析</a>');
+                $tools->append('<a href="/admin/thirdparty/logs/export" class="btn btn-sm btn-success"><i class="fa fa-download"></i> 导出日志</a>');
+                $tools->append('<a href="/admin/thirdparty/logs/cleanup" class="btn btn-sm btn-warning"><i class="fa fa-trash"></i> 清理日志</a>');
+            });
+
+            // 行操作
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                // 移除编辑按钮,日志只读
+                $actions->disableEdit();
+                
+                if ($actions->row->error_message) {
+                    $actions->append('<a href="/admin/thirdparty/logs/' . $actions->getKey() . '/retry" class="btn btn-xs btn-warning"><i class="fa fa-redo"></i> 重试</a>');
+                }
+            });
+
+            // 默认排序(最新的在前)
+            $grid->model()->orderBy('created_at', 'desc');
+            
+            // 禁用创建按钮
+            $grid->disableCreateButton();
+        });
+    }
+
+    /**
+     * 详情页面
+     *
+     * @return Show
+     */
+    protected function detail($id): Show
+    {
+        return Show::make($id, new ThirdPartyLogRepository(), function (Show $show) {
+            $show->field('id', 'ID');
+            
+            $show->field('service.name', '服务名称');
+            $show->field('service.code', '服务代码');
+            $show->field('credential.name', '使用凭证');
+            
+            $show->field('request_id', '请求ID');
+            $show->field('method', 'HTTP方法');
+            $show->field('url', '请求URL');
+
+            $show->field('headers', '请求头')->json();
+            $show->field('params', '请求参数')->json();
+            $show->field('body', '请求体')->code();
+
+            $show->field('response_status', '响应状态码');
+            $show->field('response_headers', '响应头')->json();
+            $show->field('response_body', '响应体')->code();
+            
+            $show->field('response_time', '响应时间')->as(function ($time) {
+                return $time ? $time . ' ms' : '-';
+            });
+
+            $show->field('level', '日志级别')->as(function ($level) {
+                $logLevel = LOG_LEVEL::tryFrom($level);
+                return $logLevel ? $logLevel->getLabel() : $level;
+            });
+
+            $show->field('error_message', '错误信息');
+
+            $show->field('user_id', '用户ID');
+            $show->field('ip_address', 'IP地址');
+            $show->field('user_agent', 'User Agent');
+
+            $show->field('created_at', '调用时间');
+
+            // 相关日志
+            $show->relation('relatedLogs', '相关日志', function ($model) {
+                $grid = new Grid(new ThirdPartyLog());
+                $grid->model()->where('service_id', $model->service_id)
+                       ->where('id', '!=', $model->id)
+                       ->orderBy('created_at', 'desc')
+                       ->limit(10);
+                       
+                $grid->column('request_id', '请求ID');
+                $grid->column('method', '方法');
+                $grid->column('response_status', '状态码');
+                $grid->column('response_time', '响应时间(ms)');
+                $grid->column('level', '级别');
+                $grid->column('created_at', '时间');
+                
+                $grid->disableCreateButton();
+                $grid->disableActions();
+                $grid->disableFilter();
+                $grid->disableBatchActions();
+                
+                return $grid;
+            });
+        });
+    }
+}

+ 290 - 0
app/Module/ThirdParty/AdminControllers/ThirdPartyMonitorController.php

@@ -0,0 +1,290 @@
+<?php
+
+namespace App\Module\ThirdParty\AdminControllers;
+
+use UCore\DcatAdmin\AdminController;
+use App\Module\ThirdParty\Models\ThirdPartyMonitor;
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Repositorys\ThirdPartyMonitorRepository;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+
+/**
+ * 第三方服务监控记录管理控制器
+ * 
+ * 路由: /admin/thirdparty/monitors
+ */
+class ThirdPartyMonitorController extends AdminController
+{
+    /**
+     * 页面标题
+     *
+     * @var string
+     */
+    protected $title = '监控记录管理';
+
+    /**
+     * 数据仓库
+     *
+     * @return string
+     */
+    protected function repository()
+    {
+        return ThirdPartyMonitorRepository::class;
+    }
+
+    /**
+     * 列表页面
+     *
+     * @return Grid
+     */
+    protected function grid(): Grid
+    {
+        return Grid::make(new ThirdPartyMonitorRepository(), function (Grid $grid) {
+            // 基础设置
+            $grid->column('id', 'ID')->sortable();
+            
+            // 关联服务
+            $grid->column('service.name', '服务名称')->sortable();
+            $grid->column('service.code', '服务代码');
+            
+            // 检查类型
+            $grid->column('check_type', '检查类型')->display(function ($type) {
+                $types = [
+                    'health' => ['label' => '健康检查', 'color' => 'primary', 'icon' => 'fa-heartbeat'],
+                    'performance' => ['label' => '性能检查', 'color' => 'info', 'icon' => 'fa-tachometer-alt'],
+                    'availability' => ['label' => '可用性检查', 'color' => 'success', 'icon' => 'fa-check-circle'],
+                    'status_change' => ['label' => '状态变更', 'color' => 'warning', 'icon' => 'fa-exchange-alt'],
+                ];
+                
+                $config = $types[$type] ?? ['label' => $type, 'color' => 'secondary', 'icon' => 'fa-question'];
+                return "<span class='badge badge-{$config['color']}'><i class='{$config['icon']}'></i> {$config['label']}</span>";
+            });
+
+            // 检查状态
+            $grid->column('status', '检查状态')->display(function ($status) {
+                $colors = [
+                    'success' => 'success',
+                    'warning' => 'warning',
+                    'error' => 'danger',
+                    'timeout' => 'secondary',
+                ];
+                
+                $icons = [
+                    'success' => 'fa-check',
+                    'warning' => 'fa-exclamation-triangle',
+                    'error' => 'fa-times',
+                    'timeout' => 'fa-clock',
+                ];
+                
+                $color = $colors[$status] ?? 'secondary';
+                $icon = $icons[$status] ?? 'fa-question';
+                
+                return "<span class='badge badge-{$color}'><i class='{$icon}'></i> " . ucfirst($status) . "</span>";
+            });
+
+            // 响应时间
+            $grid->column('response_time', '响应时间')->display(function ($time) {
+                if (!$time) {
+                    return '-';
+                }
+                
+                $color = 'success';
+                if ($time > 5000) {
+                    $color = 'danger';
+                } elseif ($time > 2000) {
+                    $color = 'warning';
+                } elseif ($time > 1000) {
+                    $color = 'info';
+                }
+                
+                return "<span class='badge badge-{$color}'>{$time}ms</span>";
+            })->sortable();
+
+            // HTTP状态码
+            $grid->column('status_code', 'HTTP状态码')->display(function ($code) {
+                if (!$code) {
+                    return '-';
+                }
+                
+                $color = 'secondary';
+                if ($code >= 200 && $code < 300) {
+                    $color = 'success';
+                } elseif ($code >= 300 && $code < 400) {
+                    $color = 'info';
+                } elseif ($code >= 400 && $code < 500) {
+                    $color = 'warning';
+                } elseif ($code >= 500) {
+                    $color = 'danger';
+                }
+                
+                return "<span class='badge badge-{$color}'>{$code}</span>";
+            });
+
+            // 错误信息(截断显示)
+            $grid->column('error_message', '错误信息')->display(function ($error) {
+                if (!$error) {
+                    return '-';
+                }
+                $shortError = strlen($error) > 30 ? substr($error, 0, 30) . '...' : $error;
+                return "<span class='text-danger' title='{$error}'>{$shortError}</span>";
+            });
+
+            // 检查时间
+            $grid->column('checked_at', '检查时间')->sortable();
+
+            // 过滤器
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->equal('service_id', '服务')->select(
+                    ThirdPartyService::pluck('name', 'id')->toArray()
+                );
+                $filter->equal('check_type', '检查类型')->select([
+                    'health' => '健康检查',
+                    'performance' => '性能检查',
+                    'availability' => '可用性检查',
+                    'status_change' => '状态变更',
+                ]);
+                $filter->equal('status', '检查状态')->select([
+                    'success' => '成功',
+                    'warning' => '警告',
+                    'error' => '错误',
+                    'timeout' => '超时',
+                ]);
+                $filter->between('response_time', '响应时间(ms)');
+                $filter->between('status_code', 'HTTP状态码');
+                $filter->like('error_message', '错误信息');
+                $filter->between('checked_at', '检查时间')->datetime();
+            });
+
+            // 批量操作
+            $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
+                $batch->add('批量删除', new \App\Module\ThirdParty\AdminActions\BatchDeleteMonitorAction());
+            });
+
+            // 工具栏
+            $grid->tools(function (Grid\Tools $tools) {
+                $tools->append('<a href="/admin/thirdparty/monitors/health-check" class="btn btn-sm btn-success"><i class="fa fa-heartbeat"></i> 执行健康检查</a>');
+                $tools->append('<a href="/admin/thirdparty/monitors/stats" class="btn btn-sm btn-info"><i class="fa fa-chart-bar"></i> 监控统计</a>');
+                $tools->append('<a href="/admin/thirdparty/monitors/cleanup" class="btn btn-sm btn-warning"><i class="fa fa-trash"></i> 清理记录</a>');
+            });
+
+            // 行操作
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                // 移除编辑按钮,监控记录只读
+                $actions->disableEdit();
+                
+                if ($actions->row->status === 'error') {
+                    $actions->append('<a href="/admin/thirdparty/monitors/' . $actions->getKey() . '/recheck" class="btn btn-xs btn-warning"><i class="fa fa-redo"></i> 重新检查</a>');
+                }
+            });
+
+            // 默认排序(最新的在前)
+            $grid->model()->orderBy('checked_at', 'desc');
+            
+            // 禁用创建按钮
+            $grid->disableCreateButton();
+        });
+    }
+
+    /**
+     * 详情页面
+     *
+     * @return Show
+     */
+    protected function detail($id): Show
+    {
+        return Show::make($id, new ThirdPartyMonitorRepository(), function (Show $show) {
+            $show->field('id', 'ID');
+            
+            $show->field('service.name', '服务名称');
+            $show->field('service.code', '服务代码');
+            
+            $show->field('check_type', '检查类型')->as(function ($type) {
+                $types = [
+                    'health' => '健康检查',
+                    'performance' => '性能检查',
+                    'availability' => '可用性检查',
+                    'status_change' => '状态变更',
+                ];
+                return $types[$type] ?? $type;
+            });
+
+            $show->field('status', '检查状态')->as(function ($status) {
+                return ucfirst($status);
+            });
+
+            $show->field('response_time', '响应时间')->as(function ($time) {
+                return $time ? $time . ' ms' : '-';
+            });
+
+            $show->field('status_code', 'HTTP状态码');
+            $show->field('error_message', '错误信息');
+
+            $show->field('details', '详细信息')->json();
+
+            $show->field('checked_at', '检查时间');
+
+            // 相关监控记录
+            $show->relation('recentChecks', '最近检查记录', function ($model) {
+                $grid = new Grid(new ThirdPartyMonitor());
+                $grid->model()->where('service_id', $model->service_id)
+                       ->where('check_type', $model->check_type)
+                       ->where('id', '!=', $model->id)
+                       ->orderBy('checked_at', 'desc')
+                       ->limit(10);
+                       
+                $grid->column('status', '状态')->display(function ($status) {
+                    $colors = [
+                        'success' => 'success',
+                        'warning' => 'warning', 
+                        'error' => 'danger',
+                        'timeout' => 'secondary',
+                    ];
+                    $color = $colors[$status] ?? 'secondary';
+                    return "<span class='badge badge-{$color}'>" . ucfirst($status) . "</span>";
+                });
+                
+                $grid->column('response_time', '响应时间(ms)');
+                $grid->column('status_code', 'HTTP状态码');
+                $grid->column('checked_at', '检查时间');
+                
+                $grid->disableCreateButton();
+                $grid->disableActions();
+                $grid->disableFilter();
+                $grid->disableBatchActions();
+                
+                return $grid;
+            });
+
+            // 服务健康趋势
+            $show->relation('healthTrend', '健康趋势', function ($model) {
+                $grid = new Grid(new ThirdPartyMonitor());
+                $grid->model()->where('service_id', $model->service_id)
+                       ->where('check_type', 'health')
+                       ->orderBy('checked_at', 'desc')
+                       ->limit(20);
+                       
+                $grid->column('status', '状态')->display(function ($status) {
+                    $colors = [
+                        'success' => 'success',
+                        'warning' => 'warning',
+                        'error' => 'danger',
+                        'timeout' => 'secondary',
+                    ];
+                    $color = $colors[$status] ?? 'secondary';
+                    return "<span class='badge badge-{$color}'>" . ucfirst($status) . "</span>";
+                });
+                
+                $grid->column('response_time', '响应时间(ms)');
+                $grid->column('checked_at', '检查时间');
+                
+                $grid->disableCreateButton();
+                $grid->disableActions();
+                $grid->disableFilter();
+                $grid->disableBatchActions();
+                
+                return $grid;
+            });
+        });
+    }
+}

+ 307 - 0
app/Module/ThirdParty/AdminControllers/ThirdPartyQuotaController.php

@@ -0,0 +1,307 @@
+<?php
+
+namespace App\Module\ThirdParty\AdminControllers;
+
+use UCore\DcatAdmin\AdminController;
+use App\Module\ThirdParty\Models\ThirdPartyQuota;
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Repositorys\ThirdPartyQuotaRepository;
+use App\Module\ThirdParty\Enums\QUOTA_TYPE;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+
+/**
+ * 第三方服务配额管理控制器
+ * 
+ * 路由: /admin/thirdparty/quotas
+ */
+class ThirdPartyQuotaController extends AdminController
+{
+    /**
+     * 页面标题
+     *
+     * @var string
+     */
+    protected $title = '配额管理';
+
+    /**
+     * 数据仓库
+     *
+     * @return string
+     */
+    protected function repository()
+    {
+        return ThirdPartyQuotaRepository::class;
+    }
+
+    /**
+     * 列表页面
+     *
+     * @return Grid
+     */
+    protected function grid(): Grid
+    {
+        return Grid::make(new ThirdPartyQuotaRepository(), function (Grid $grid) {
+            // 基础设置
+            $grid->column('id', 'ID')->sortable();
+            
+            // 关联服务
+            $grid->column('service.name', '服务名称')->sortable();
+            $grid->column('service.code', '服务代码');
+            
+            // 配额类型
+            $grid->column('type', '配额类型')->display(function ($type) {
+                $quotaType = QUOTA_TYPE::tryFrom($type);
+                if ($quotaType) {
+                    $label = $quotaType->getLabel();
+                    $color = $quotaType->getColor();
+                    return "<span class='badge badge-{$color}'>{$label}</span>";
+                }
+                return $type;
+            });
+
+            // 配额限制
+            $grid->column('limit_value', '配额限制')->display(function ($limit) {
+                return number_format($limit);
+            })->sortable();
+
+            // 已使用量
+            $grid->column('used_value', '已使用')->display(function ($used) {
+                return number_format($used);
+            })->sortable();
+
+            // 使用率
+            $grid->column('usage_percentage', '使用率')->display(function () {
+                $percentage = $this->limit_value > 0 ? 
+                    ($this->used_value / $this->limit_value) * 100 : 0;
+                
+                $color = 'success';
+                if ($percentage >= 95) {
+                    $color = 'danger';
+                } elseif ($percentage >= 80) {
+                    $color = 'warning';
+                } elseif ($percentage >= 60) {
+                    $color = 'info';
+                }
+                
+                return "<div class='progress' style='height: 20px;'>
+                    <div class='progress-bar bg-{$color}' style='width: {$percentage}%'>
+                        " . number_format($percentage, 1) . "%
+                    </div>
+                </div>";
+            });
+
+            // 剩余配额
+            $grid->column('remaining', '剩余配额')->display(function () {
+                $remaining = max(0, $this->limit_value - $this->used_value);
+                $color = $remaining > 0 ? 'text-success' : 'text-danger';
+                return "<span class='{$color}'>" . number_format($remaining) . "</span>";
+            });
+
+            // 时间窗口
+            $grid->column('window_start', '窗口开始')->display(function ($time) {
+                return $time ? \Carbon\Carbon::parse($time)->format('m-d H:i') : '-';
+            });
+            
+            $grid->column('window_end', '窗口结束')->display(function ($time) {
+                return $time ? \Carbon\Carbon::parse($time)->format('m-d H:i') : '-';
+            });
+
+            // 重置时间
+            $grid->column('reset_at', '下次重置')->display(function ($time) {
+                if (!$time) {
+                    return '-';
+                }
+                
+                $resetTime = \Carbon\Carbon::parse($time);
+                if ($resetTime->isPast()) {
+                    return '<span class="badge badge-warning">待重置</span>';
+                } else {
+                    return $resetTime->diffForHumans();
+                }
+            });
+
+            // 告警阈值
+            $grid->column('alert_threshold', '告警阈值')->display(function ($threshold) {
+                return $threshold . '%';
+            });
+
+            // 状态
+            $grid->column('is_active', '状态')->display(function ($active) {
+                return $active ? 
+                    '<span class="badge badge-success"><i class="fa fa-check"></i> 激活</span>' : 
+                    '<span class="badge badge-secondary"><i class="fa fa-times"></i> 未激活</span>';
+            });
+
+            // 超限状态
+            $grid->column('is_exceeded', '超限状态')->display(function ($exceeded) {
+                return $exceeded ? 
+                    '<span class="badge badge-danger"><i class="fa fa-exclamation-triangle"></i> 已超限</span>' : 
+                    '<span class="badge badge-success"><i class="fa fa-check"></i> 正常</span>';
+            });
+
+            // 过滤器
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->equal('service_id', '服务')->select(
+                    ThirdPartyService::pluck('name', 'id')->toArray()
+                );
+                $filter->equal('type', '配额类型')->select(QUOTA_TYPE::getOptions());
+                $filter->equal('is_active', '状态')->select([
+                    1 => '激活',
+                    0 => '未激活',
+                ]);
+                $filter->equal('is_exceeded', '超限状态')->select([
+                    1 => '已超限',
+                    0 => '正常',
+                ]);
+                $filter->between('alert_threshold', '告警阈值(%)');
+                $filter->between('used_value', '已使用量');
+            });
+
+            // 批量操作
+            $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
+                $batch->add('批量重置', new \App\Module\ThirdParty\AdminActions\BatchResetQuotaAction());
+                $batch->add('批量激活', new \App\Module\ThirdParty\AdminActions\BatchActivateQuotaAction());
+                $batch->add('批量停用', new \App\Module\ThirdParty\AdminActions\BatchDeactivateQuotaAction());
+            });
+
+            // 工具栏
+            $grid->tools(function (Grid\Tools $tools) {
+                $tools->append('<a href="/admin/thirdparty/quotas/exceeded" class="btn btn-sm btn-danger"><i class="fa fa-exclamation-triangle"></i> 超限配额</a>');
+                $tools->append('<a href="/admin/thirdparty/quotas/reset-all" class="btn btn-sm btn-warning"><i class="fa fa-redo"></i> 重置所有</a>');
+                $tools->append('<a href="/admin/thirdparty/quotas/stats" class="btn btn-sm btn-info"><i class="fa fa-chart-bar"></i> 使用统计</a>');
+            });
+
+            // 行操作
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                $actions->append('<a href="/admin/thirdparty/quotas/' . $actions->getKey() . '/reset" class="btn btn-xs btn-warning"><i class="fa fa-redo"></i> 重置</a>');
+                
+                if ($actions->row->is_exceeded) {
+                    $actions->append('<a href="/admin/thirdparty/quotas/' . $actions->getKey() . '/increase" class="btn btn-xs btn-success"><i class="fa fa-plus"></i> 增加配额</a>');
+                }
+                
+                $actions->append('<a href="/admin/thirdparty/quotas/' . $actions->getKey() . '/usage" class="btn btn-xs btn-info"><i class="fa fa-chart-line"></i> 使用详情</a>');
+            });
+
+            // 默认排序
+            $grid->model()->orderBy('service_id')->orderBy('type');
+        });
+    }
+
+    /**
+     * 详情页面
+     *
+     * @return Show
+     */
+    protected function detail($id): Show
+    {
+        return Show::make($id, new ThirdPartyQuotaRepository(), function (Show $show) {
+            $show->field('id', 'ID');
+            
+            $show->field('service.name', '服务名称');
+            $show->field('service.code', '服务代码');
+            
+            $show->field('type', '配额类型')->as(function ($type) {
+                $quotaType = QUOTA_TYPE::tryFrom($type);
+                return $quotaType ? $quotaType->getLabel() : $type;
+            });
+
+            $show->field('limit_value', '配额限制')->as(function ($limit) {
+                return number_format($limit);
+            });
+
+            $show->field('used_value', '已使用量')->as(function ($used) {
+                return number_format($used);
+            });
+
+            $show->field('usage_percentage', '使用率')->as(function () {
+                $percentage = $this->limit_value > 0 ? 
+                    ($this->used_value / $this->limit_value) * 100 : 0;
+                return number_format($percentage, 2) . '%';
+            });
+
+            $show->field('remaining', '剩余配额')->as(function () {
+                $remaining = max(0, $this->limit_value - $this->used_value);
+                return number_format($remaining);
+            });
+
+            $show->field('window_start', '时间窗口开始');
+            $show->field('window_end', '时间窗口结束');
+            $show->field('reset_at', '下次重置时间');
+
+            $show->field('alert_threshold', '告警阈值')->as(function ($threshold) {
+                return $threshold . '%';
+            });
+
+            $show->field('is_active', '状态')->as(function ($active) {
+                return $active ? '激活' : '未激活';
+            });
+
+            $show->field('is_exceeded', '超限状态')->as(function ($exceeded) {
+                return $exceeded ? '已超限' : '正常';
+            });
+
+            $show->field('exceeded_at', '超限时间');
+
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+        });
+    }
+
+    /**
+     * 表单页面
+     *
+     * @return Form
+     */
+    protected function form(): Form
+    {
+        return Form::make(new ThirdPartyQuotaRepository(), function (Form $form) {
+            $form->display('id', 'ID');
+
+            $form->select('service_id', '所属服务')
+                ->options(ThirdPartyService::pluck('name', 'id')->toArray())
+                ->required()
+                ->help('选择要配置配额的第三方服务');
+
+            $form->select('type', '配额类型')
+                ->options(QUOTA_TYPE::getOptions())
+                ->required()
+                ->help('选择配额的时间窗口类型');
+
+            $form->number('limit_value', '配额限制')
+                ->required()
+                ->min(1)
+                ->help('在指定时间窗口内允许的最大调用次数');
+
+            $form->number('alert_threshold', '告警阈值(%)')
+                ->default(80)
+                ->min(1)
+                ->max(100)
+                ->help('当使用率达到此百分比时触发告警');
+
+            $form->switch('is_active', '是否激活')->default(1);
+
+            $form->datetime('window_start', '时间窗口开始')->help('留空自动计算');
+            $form->datetime('window_end', '时间窗口结束')->help('留空自动计算');
+            $form->datetime('reset_at', '下次重置时间')->help('留空自动计算');
+
+            $form->display('used_value', '已使用量');
+            $form->display('is_exceeded', '超限状态');
+            $form->display('exceeded_at', '超限时间');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            // 保存前处理
+            $form->saving(function (Form $form) {
+                // 自动计算时间窗口
+                if (empty($form->window_start) || empty($form->window_end)) {
+                    $quotaType = QUOTA_TYPE::from($form->type);
+                    $form->window_start = $quotaType->getCurrentWindowStart()->format('Y-m-d H:i:s');
+                    $form->window_end = $quotaType->getCurrentWindowEnd()->format('Y-m-d H:i:s');
+                    $form->reset_at = $form->window_end;
+                }
+            });
+        });
+    }
+}

+ 210 - 0
app/Module/ThirdParty/Events/ApiCallEvent.php

@@ -0,0 +1,210 @@
+<?php
+
+namespace App\Module\ThirdParty\Events;
+
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Models\ThirdPartyCredential;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * API调用事件
+ * 
+ * 当第三方API被调用时触发此事件
+ */
+class ApiCallEvent
+{
+    use Dispatchable, SerializesModels;
+
+    /**
+     * 第三方服务实例
+     *
+     * @var ThirdPartyService
+     */
+    public ThirdPartyService $service;
+
+    /**
+     * 认证凭证实例
+     *
+     * @var ThirdPartyCredential
+     */
+    public ThirdPartyCredential $credential;
+
+    /**
+     * 请求ID
+     *
+     * @var string
+     */
+    public string $requestId;
+
+    /**
+     * HTTP方法
+     *
+     * @var string
+     */
+    public string $method;
+
+    /**
+     * 请求URL
+     *
+     * @var string
+     */
+    public string $url;
+
+    /**
+     * 请求头
+     *
+     * @var array
+     */
+    public array $headers;
+
+    /**
+     * 请求参数
+     *
+     * @var array
+     */
+    public array $params;
+
+    /**
+     * 响应数据
+     *
+     * @var array|null
+     */
+    public ?array $response;
+
+    /**
+     * 响应状态码
+     *
+     * @var int|null
+     */
+    public ?int $statusCode;
+
+    /**
+     * 响应时间(毫秒)
+     *
+     * @var int
+     */
+    public int $responseTime;
+
+    /**
+     * 是否成功
+     *
+     * @var bool
+     */
+    public bool $success;
+
+    /**
+     * 错误信息
+     *
+     * @var string|null
+     */
+    public ?string $errorMessage;
+
+    /**
+     * 创建事件实例
+     *
+     * @param ThirdPartyService $service
+     * @param ThirdPartyCredential $credential
+     * @param string $requestId
+     * @param string $method
+     * @param string $url
+     * @param array $headers
+     * @param array $params
+     * @param array|null $response
+     * @param int|null $statusCode
+     * @param int $responseTime
+     * @param bool $success
+     * @param string|null $errorMessage
+     */
+    public function __construct(
+        ThirdPartyService $service,
+        ThirdPartyCredential $credential,
+        string $requestId,
+        string $method,
+        string $url,
+        array $headers,
+        array $params,
+        ?array $response = null,
+        ?int $statusCode = null,
+        int $responseTime = 0,
+        bool $success = true,
+        ?string $errorMessage = null
+    ) {
+        $this->service = $service;
+        $this->credential = $credential;
+        $this->requestId = $requestId;
+        $this->method = $method;
+        $this->url = $url;
+        $this->headers = $headers;
+        $this->params = $params;
+        $this->response = $response;
+        $this->statusCode = $statusCode;
+        $this->responseTime = $responseTime;
+        $this->success = $success;
+        $this->errorMessage = $errorMessage;
+    }
+
+    /**
+     * 获取事件数据数组
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return [
+            'service_id' => $this->service->id,
+            'service_code' => $this->service->code,
+            'credential_id' => $this->credential->id,
+            'request_id' => $this->requestId,
+            'method' => $this->method,
+            'url' => $this->url,
+            'headers' => $this->headers,
+            'params' => $this->params,
+            'response' => $this->response,
+            'status_code' => $this->statusCode,
+            'response_time' => $this->responseTime,
+            'success' => $this->success,
+            'error_message' => $this->errorMessage,
+            'timestamp' => now()->toISOString(),
+        ];
+    }
+
+    /**
+     * 判断是否为错误调用
+     *
+     * @return bool
+     */
+    public function isError(): bool
+    {
+        return !$this->success || ($this->statusCode && $this->statusCode >= 400);
+    }
+
+    /**
+     * 判断是否为慢调用
+     *
+     * @param int $threshold 阈值(毫秒)
+     * @return bool
+     */
+    public function isSlowCall(int $threshold = 5000): bool
+    {
+        return $this->responseTime > $threshold;
+    }
+
+    /**
+     * 获取日志级别
+     *
+     * @return string
+     */
+    public function getLogLevel(): string
+    {
+        if ($this->isError()) {
+            return 'ERROR';
+        }
+        
+        if ($this->isSlowCall()) {
+            return 'WARNING';
+        }
+        
+        return 'INFO';
+    }
+}

+ 297 - 0
app/Module/ThirdParty/Events/MonitorAlertEvent.php

@@ -0,0 +1,297 @@
+<?php
+
+namespace App\Module\ThirdParty\Events;
+
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Models\ThirdPartyMonitor;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 监控告警事件
+ * 
+ * 当第三方服务监控检测到异常时触发此事件
+ */
+class MonitorAlertEvent
+{
+    use Dispatchable, SerializesModels;
+
+    /**
+     * 第三方服务实例
+     *
+     * @var ThirdPartyService
+     */
+    public ThirdPartyService $service;
+
+    /**
+     * 监控记录实例
+     *
+     * @var ThirdPartyMonitor
+     */
+    public ThirdPartyMonitor $monitor;
+
+    /**
+     * 告警类型
+     *
+     * @var string
+     */
+    public string $alertType;
+
+    /**
+     * 告警级别
+     *
+     * @var string
+     */
+    public string $alertLevel;
+
+    /**
+     * 告警消息
+     *
+     * @var string
+     */
+    public string $alertMessage;
+
+    /**
+     * 监控数据
+     *
+     * @var array
+     */
+    public array $monitorData;
+
+    /**
+     * 告警时间
+     *
+     * @var \Carbon\Carbon
+     */
+    public \Carbon\Carbon $alertTime;
+
+    /**
+     * 额外数据
+     *
+     * @var array
+     */
+    public array $metadata;
+
+    // 告警类型常量
+    public const ALERT_TYPE_HEALTH = 'health';           // 健康检查告警
+    public const ALERT_TYPE_PERFORMANCE = 'performance'; // 性能告警
+    public const ALERT_TYPE_AVAILABILITY = 'availability'; // 可用性告警
+    public const ALERT_TYPE_ERROR_RATE = 'error_rate';   // 错误率告警
+    public const ALERT_TYPE_RESPONSE_TIME = 'response_time'; // 响应时间告警
+
+    // 告警级别常量
+    public const LEVEL_INFO = 'INFO';
+    public const LEVEL_WARNING = 'WARNING';
+    public const LEVEL_CRITICAL = 'CRITICAL';
+
+    /**
+     * 创建事件实例
+     *
+     * @param ThirdPartyService $service
+     * @param ThirdPartyMonitor $monitor
+     * @param string $alertType
+     * @param string $alertLevel
+     * @param string $alertMessage
+     * @param array $monitorData
+     * @param array $metadata
+     */
+    public function __construct(
+        ThirdPartyService $service,
+        ThirdPartyMonitor $monitor,
+        string $alertType,
+        string $alertLevel,
+        string $alertMessage,
+        array $monitorData = [],
+        array $metadata = []
+    ) {
+        $this->service = $service;
+        $this->monitor = $monitor;
+        $this->alertType = $alertType;
+        $this->alertLevel = $alertLevel;
+        $this->alertMessage = $alertMessage;
+        $this->monitorData = $monitorData;
+        $this->alertTime = now();
+        $this->metadata = $metadata;
+    }
+
+    /**
+     * 获取事件数据数组
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return [
+            'service_id' => $this->service->id,
+            'service_code' => $this->service->code,
+            'service_name' => $this->service->name,
+            'monitor_id' => $this->monitor->id,
+            'alert_type' => $this->alertType,
+            'alert_level' => $this->alertLevel,
+            'alert_message' => $this->alertMessage,
+            'monitor_data' => $this->monitorData,
+            'alert_time' => $this->alertTime->toISOString(),
+            'metadata' => $this->metadata,
+        ];
+    }
+
+    /**
+     * 判断是否为严重告警
+     *
+     * @return bool
+     */
+    public function isCritical(): bool
+    {
+        return $this->alertLevel === self::LEVEL_CRITICAL;
+    }
+
+    /**
+     * 判断是否为警告告警
+     *
+     * @return bool
+     */
+    public function isWarning(): bool
+    {
+        return $this->alertLevel === self::LEVEL_WARNING;
+    }
+
+    /**
+     * 获取告警标题
+     *
+     * @return string
+     */
+    public function getAlertTitle(): string
+    {
+        $serviceName = $this->service->name;
+        $typeLabel = $this->getAlertTypeLabel();
+
+        return "第三方服务监控告警 - {$serviceName} ({$typeLabel})";
+    }
+
+    /**
+     * 获取告警类型标签
+     *
+     * @return string
+     */
+    public function getAlertTypeLabel(): string
+    {
+        return match ($this->alertType) {
+            self::ALERT_TYPE_HEALTH => '健康检查',
+            self::ALERT_TYPE_PERFORMANCE => '性能监控',
+            self::ALERT_TYPE_AVAILABILITY => '可用性监控',
+            self::ALERT_TYPE_ERROR_RATE => '错误率监控',
+            self::ALERT_TYPE_RESPONSE_TIME => '响应时间监控',
+            default => '未知类型',
+        };
+    }
+
+    /**
+     * 获取详细告警信息
+     *
+     * @return array
+     */
+    public function getDetailedInfo(): array
+    {
+        $info = [
+            'service' => [
+                'id' => $this->service->id,
+                'code' => $this->service->code,
+                'name' => $this->service->name,
+                'type' => $this->service->type,
+                'status' => $this->service->status,
+            ],
+            'alert' => [
+                'type' => $this->alertType,
+                'type_label' => $this->getAlertTypeLabel(),
+                'level' => $this->alertLevel,
+                'message' => $this->alertMessage,
+                'time' => $this->alertTime->toISOString(),
+            ],
+            'monitor' => [
+                'id' => $this->monitor->id,
+                'check_type' => $this->monitor->check_type,
+                'status' => $this->monitor->status,
+                'response_time' => $this->monitor->response_time,
+                'checked_at' => $this->monitor->checked_at?->toISOString(),
+            ],
+            'data' => $this->monitorData,
+            'metadata' => $this->metadata,
+        ];
+
+        return $info;
+    }
+
+    /**
+     * 获取建议操作
+     *
+     * @return array
+     */
+    public function getSuggestedActions(): array
+    {
+        $actions = [];
+
+        switch ($this->alertType) {
+            case self::ALERT_TYPE_HEALTH:
+                $actions[] = '检查服务健康状态';
+                $actions[] = '验证服务配置';
+                $actions[] = '检查网络连接';
+                if ($this->isCritical()) {
+                    $actions[] = '启用备用服务';
+                    $actions[] = '通知相关人员';
+                }
+                break;
+
+            case self::ALERT_TYPE_PERFORMANCE:
+                $actions[] = '分析性能瓶颈';
+                $actions[] = '检查服务负载';
+                $actions[] = '优化调用频率';
+                if ($this->isCritical()) {
+                    $actions[] = '考虑降级服务';
+                }
+                break;
+
+            case self::ALERT_TYPE_AVAILABILITY:
+                $actions[] = '检查服务可用性';
+                $actions[] = '验证服务状态';
+                $actions[] = '检查依赖服务';
+                if ($this->isCritical()) {
+                    $actions[] = '切换到备用服务';
+                }
+                break;
+
+            case self::ALERT_TYPE_ERROR_RATE:
+                $actions[] = '分析错误日志';
+                $actions[] = '检查调用参数';
+                $actions[] = '验证认证信息';
+                if ($this->isCritical()) {
+                    $actions[] = '暂停相关调用';
+                }
+                break;
+
+            case self::ALERT_TYPE_RESPONSE_TIME:
+                $actions[] = '检查网络延迟';
+                $actions[] = '分析服务响应';
+                $actions[] = '优化请求参数';
+                if ($this->isCritical()) {
+                    $actions[] = '增加超时时间';
+                    $actions[] = '考虑异步处理';
+                }
+                break;
+        }
+
+        return $actions;
+    }
+
+    /**
+     * 判断是否需要立即处理
+     *
+     * @return bool
+     */
+    public function requiresImmediateAction(): bool
+    {
+        return $this->isCritical() && in_array($this->alertType, [
+            self::ALERT_TYPE_HEALTH,
+            self::ALERT_TYPE_AVAILABILITY,
+        ]);
+    }
+}

+ 266 - 0
app/Module/ThirdParty/Events/QuotaAlertEvent.php

@@ -0,0 +1,266 @@
+<?php
+
+namespace App\Module\ThirdParty\Events;
+
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Models\ThirdPartyQuota;
+use App\Module\ThirdParty\Enums\QUOTA_TYPE;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 配额告警事件
+ * 
+ * 当第三方服务配额达到告警阈值时触发此事件
+ */
+class QuotaAlertEvent
+{
+    use Dispatchable, SerializesModels;
+
+    /**
+     * 第三方服务实例
+     *
+     * @var ThirdPartyService
+     */
+    public ThirdPartyService $service;
+
+    /**
+     * 配额实例
+     *
+     * @var ThirdPartyQuota
+     */
+    public ThirdPartyQuota $quota;
+
+    /**
+     * 告警类型
+     *
+     * @var string
+     */
+    public string $alertType;
+
+    /**
+     * 当前使用量
+     *
+     * @var int
+     */
+    public int $currentUsage;
+
+    /**
+     * 配额限制
+     *
+     * @var int
+     */
+    public int $quotaLimit;
+
+    /**
+     * 使用百分比
+     *
+     * @var float
+     */
+    public float $usagePercentage;
+
+    /**
+     * 告警阈值
+     *
+     * @var float
+     */
+    public float $alertThreshold;
+
+    /**
+     * 告警时间
+     *
+     * @var \Carbon\Carbon
+     */
+    public \Carbon\Carbon $alertTime;
+
+    /**
+     * 额外数据
+     *
+     * @var array
+     */
+    public array $metadata;
+
+    // 告警类型常量
+    public const ALERT_TYPE_WARNING = 'warning';      // 警告:达到警告阈值
+    public const ALERT_TYPE_CRITICAL = 'critical';    // 严重:达到严重阈值
+    public const ALERT_TYPE_EXHAUSTED = 'exhausted';  // 耗尽:配额用完
+
+    /**
+     * 创建事件实例
+     *
+     * @param ThirdPartyService $service
+     * @param ThirdPartyQuota $quota
+     * @param string $alertType
+     * @param int $currentUsage
+     * @param int $quotaLimit
+     * @param float $usagePercentage
+     * @param float $alertThreshold
+     * @param array $metadata
+     */
+    public function __construct(
+        ThirdPartyService $service,
+        ThirdPartyQuota $quota,
+        string $alertType,
+        int $currentUsage,
+        int $quotaLimit,
+        float $usagePercentage,
+        float $alertThreshold,
+        array $metadata = []
+    ) {
+        $this->service = $service;
+        $this->quota = $quota;
+        $this->alertType = $alertType;
+        $this->currentUsage = $currentUsage;
+        $this->quotaLimit = $quotaLimit;
+        $this->usagePercentage = $usagePercentage;
+        $this->alertThreshold = $alertThreshold;
+        $this->alertTime = now();
+        $this->metadata = $metadata;
+    }
+
+    /**
+     * 获取事件数据数组
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return [
+            'service_id' => $this->service->id,
+            'service_code' => $this->service->code,
+            'service_name' => $this->service->name,
+            'quota_id' => $this->quota->id,
+            'quota_type' => $this->quota->quota_type,
+            'quota_type_label' => QUOTA_TYPE::from($this->quota->quota_type)->getLabel(),
+            'alert_type' => $this->alertType,
+            'current_usage' => $this->currentUsage,
+            'quota_limit' => $this->quotaLimit,
+            'usage_percentage' => $this->usagePercentage,
+            'alert_threshold' => $this->alertThreshold,
+            'alert_time' => $this->alertTime->toISOString(),
+            'metadata' => $this->metadata,
+        ];
+    }
+
+    /**
+     * 获取告警级别
+     *
+     * @return string
+     */
+    public function getAlertLevel(): string
+    {
+        return match ($this->alertType) {
+            self::ALERT_TYPE_WARNING => 'WARNING',
+            self::ALERT_TYPE_CRITICAL => 'CRITICAL',
+            self::ALERT_TYPE_EXHAUSTED => 'CRITICAL',
+            default => 'INFO',
+        };
+    }
+
+    /**
+     * 获取告警消息
+     *
+     * @return string
+     */
+    public function getAlertMessage(): string
+    {
+        $serviceName = $this->service->name;
+        $quotaTypeLabel = QUOTA_TYPE::from($this->quota->quota_type)->getLabel();
+        $percentage = number_format($this->usagePercentage, 1);
+
+        return match ($this->alertType) {
+            self::ALERT_TYPE_WARNING => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额使用率已达到 {$percentage}%,请注意监控",
+            self::ALERT_TYPE_CRITICAL => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额使用率已达到 {$percentage}%,即将耗尽",
+            self::ALERT_TYPE_EXHAUSTED => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额已耗尽,服务调用将被限制",
+            default => "第三方服务 [{$serviceName}] 配额状态更新",
+        };
+    }
+
+    /**
+     * 获取剩余配额
+     *
+     * @return int
+     */
+    public function getRemainingQuota(): int
+    {
+        return max(0, $this->quotaLimit - $this->currentUsage);
+    }
+
+    /**
+     * 判断是否为严重告警
+     *
+     * @return bool
+     */
+    public function isCritical(): bool
+    {
+        return in_array($this->alertType, [self::ALERT_TYPE_CRITICAL, self::ALERT_TYPE_EXHAUSTED]);
+    }
+
+    /**
+     * 获取预计耗尽时间
+     *
+     * @return \Carbon\Carbon|null
+     */
+    public function getEstimatedExhaustionTime(): ?\Carbon\Carbon
+    {
+        if ($this->alertType === self::ALERT_TYPE_EXHAUSTED) {
+            return null; // 已经耗尽
+        }
+
+        // 基于当前使用率估算耗尽时间
+        $quotaType = QUOTA_TYPE::from($this->quota->quota_type);
+        $windowStart = \Carbon\Carbon::instance($quotaType->getCurrentWindowStart());
+        $windowEnd = \Carbon\Carbon::instance($quotaType->getCurrentWindowEnd());
+
+        $elapsedTime = now()->diffInMinutes($windowStart);
+        
+        if ($elapsedTime <= 0 || $this->currentUsage <= 0) {
+            return null;
+        }
+
+        $usageRate = $this->currentUsage / $elapsedTime; // 每分钟使用量
+        $remainingQuota = $this->getRemainingQuota();
+        
+        if ($usageRate <= 0) {
+            return null;
+        }
+
+        $minutesToExhaustion = $remainingQuota / $usageRate;
+        
+        return now()->addMinutes($minutesToExhaustion);
+    }
+
+    /**
+     * 获取建议操作
+     *
+     * @return array
+     */
+    public function getSuggestedActions(): array
+    {
+        $actions = [];
+
+        switch ($this->alertType) {
+            case self::ALERT_TYPE_WARNING:
+                $actions[] = '监控配额使用情况';
+                $actions[] = '检查是否有异常调用';
+                $actions[] = '考虑优化调用频率';
+                break;
+
+            case self::ALERT_TYPE_CRITICAL:
+                $actions[] = '立即检查调用情况';
+                $actions[] = '暂停非必要的API调用';
+                $actions[] = '联系服务提供商增加配额';
+                $actions[] = '启用备用服务(如有)';
+                break;
+
+            case self::ALERT_TYPE_EXHAUSTED:
+                $actions[] = '停止所有API调用';
+                $actions[] = '等待配额重置';
+                $actions[] = '切换到备用服务';
+                $actions[] = '联系服务提供商紧急增加配额';
+                break;
+        }
+
+        return $actions;
+    }
+}

+ 195 - 0
app/Module/ThirdParty/Events/ServiceStatusChangedEvent.php

@@ -0,0 +1,195 @@
+<?php
+
+namespace App\Module\ThirdParty\Events;
+
+use App\Module\ThirdParty\Models\ThirdPartyService;
+use App\Module\ThirdParty\Enums\SERVICE_STATUS;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 服务状态变更事件
+ * 
+ * 当第三方服务状态发生变更时触发此事件
+ */
+class ServiceStatusChangedEvent
+{
+    use Dispatchable, SerializesModels;
+
+    /**
+     * 第三方服务实例
+     *
+     * @var ThirdPartyService
+     */
+    public ThirdPartyService $service;
+
+    /**
+     * 原状态
+     *
+     * @var SERVICE_STATUS
+     */
+    public SERVICE_STATUS $oldStatus;
+
+    /**
+     * 新状态
+     *
+     * @var SERVICE_STATUS
+     */
+    public SERVICE_STATUS $newStatus;
+
+    /**
+     * 状态变更原因
+     *
+     * @var string|null
+     */
+    public ?string $reason;
+
+    /**
+     * 变更时间
+     *
+     * @var \Carbon\Carbon
+     */
+    public \Carbon\Carbon $changedAt;
+
+    /**
+     * 额外数据
+     *
+     * @var array
+     */
+    public array $metadata;
+
+    /**
+     * 创建事件实例
+     *
+     * @param ThirdPartyService $service
+     * @param SERVICE_STATUS $oldStatus
+     * @param SERVICE_STATUS $newStatus
+     * @param string|null $reason
+     * @param array $metadata
+     */
+    public function __construct(
+        ThirdPartyService $service,
+        SERVICE_STATUS $oldStatus,
+        SERVICE_STATUS $newStatus,
+        ?string $reason = null,
+        array $metadata = []
+    ) {
+        $this->service = $service;
+        $this->oldStatus = $oldStatus;
+        $this->newStatus = $newStatus;
+        $this->reason = $reason;
+        $this->changedAt = now();
+        $this->metadata = $metadata;
+    }
+
+    /**
+     * 获取事件数据数组
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return [
+            'service_id' => $this->service->id,
+            'service_code' => $this->service->code,
+            'service_name' => $this->service->name,
+            'old_status' => $this->oldStatus->value,
+            'old_status_label' => $this->oldStatus->getLabel(),
+            'new_status' => $this->newStatus->value,
+            'new_status_label' => $this->newStatus->getLabel(),
+            'reason' => $this->reason,
+            'changed_at' => $this->changedAt->toISOString(),
+            'metadata' => $this->metadata,
+        ];
+    }
+
+    /**
+     * 判断是否为严重状态变更
+     *
+     * @return bool
+     */
+    public function isCriticalChange(): bool
+    {
+        // 从正常状态变为错误状态
+        if ($this->oldStatus === SERVICE_STATUS::ACTIVE && 
+            in_array($this->newStatus, [SERVICE_STATUS::ERROR, SERVICE_STATUS::DISABLED])) {
+            return true;
+        }
+
+        // 变为过期状态
+        if ($this->newStatus === SERVICE_STATUS::EXPIRED) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 判断是否为恢复状态变更
+     *
+     * @return bool
+     */
+    public function isRecoveryChange(): bool
+    {
+        // 从错误状态恢复到正常状态
+        if (in_array($this->oldStatus, [SERVICE_STATUS::ERROR, SERVICE_STATUS::DISABLED, SERVICE_STATUS::MAINTENANCE]) &&
+            $this->newStatus === SERVICE_STATUS::ACTIVE) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取告警级别
+     *
+     * @return string
+     */
+    public function getAlertLevel(): string
+    {
+        if ($this->isCriticalChange()) {
+            return 'CRITICAL';
+        }
+
+        if ($this->isRecoveryChange()) {
+            return 'INFO';
+        }
+
+        if ($this->newStatus === SERVICE_STATUS::MAINTENANCE) {
+            return 'WARNING';
+        }
+
+        return 'INFO';
+    }
+
+    /**
+     * 获取通知消息
+     *
+     * @return string
+     */
+    public function getNotificationMessage(): string
+    {
+        $serviceName = $this->service->name;
+        $oldLabel = $this->oldStatus->getLabel();
+        $newLabel = $this->newStatus->getLabel();
+
+        $message = "第三方服务 [{$serviceName}] 状态从 [{$oldLabel}] 变更为 [{$newLabel}]";
+
+        if ($this->reason) {
+            $message .= ",原因:{$this->reason}";
+        }
+
+        return $message;
+    }
+
+    /**
+     * 判断是否需要发送通知
+     *
+     * @return bool
+     */
+    public function shouldNotify(): bool
+    {
+        // 严重变更或恢复变更需要通知
+        return $this->isCriticalChange() || $this->isRecoveryChange();
+    }
+}

+ 469 - 0
app/Module/ThirdParty/Listeners/AlertNotificationListener.php

@@ -0,0 +1,469 @@
+<?php
+
+namespace App\Module\ThirdParty\Listeners;
+
+use App\Module\ThirdParty\Events\QuotaAlertEvent;
+use App\Module\ThirdParty\Events\MonitorAlertEvent;
+use App\Module\ThirdParty\Events\ServiceStatusChangedEvent;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Mail;
+use Illuminate\Support\Facades\Http;
+
+/**
+ * 告警通知监听器
+ * 
+ * 监听各种告警事件并发送通知
+ */
+class AlertNotificationListener implements ShouldQueue
+{
+    use InteractsWithQueue;
+
+    /**
+     * 队列名称
+     *
+     * @var string
+     */
+    public $queue = 'notifications';
+
+    /**
+     * 处理配额告警事件
+     *
+     * @param QuotaAlertEvent $event
+     * @return void
+     */
+    public function handleQuotaAlert(QuotaAlertEvent $event): void
+    {
+        try {
+            $this->sendQuotaAlertNotification($event);
+        } catch (\Exception $e) {
+            Log::error('配额告警通知发送失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+            ]);
+        }
+    }
+
+    /**
+     * 处理监控告警事件
+     *
+     * @param MonitorAlertEvent $event
+     * @return void
+     */
+    public function handleMonitorAlert(MonitorAlertEvent $event): void
+    {
+        try {
+            $this->sendMonitorAlertNotification($event);
+        } catch (\Exception $e) {
+            Log::error('监控告警通知发送失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+            ]);
+        }
+    }
+
+    /**
+     * 处理服务状态变更事件
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    public function handleServiceStatusChanged(ServiceStatusChangedEvent $event): void
+    {
+        try {
+            if ($event->shouldNotify()) {
+                $this->sendServiceStatusNotification($event);
+            }
+        } catch (\Exception $e) {
+            Log::error('服务状态变更通知发送失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+            ]);
+        }
+    }
+
+    /**
+     * 发送配额告警通知
+     *
+     * @param QuotaAlertEvent $event
+     * @return void
+     */
+    protected function sendQuotaAlertNotification(QuotaAlertEvent $event): void
+    {
+        $channels = $this->getNotificationChannels($event->getAlertLevel());
+        
+        foreach ($channels as $channel) {
+            switch ($channel) {
+                case 'email':
+                    $this->sendEmailNotification($event);
+                    break;
+                case 'sms':
+                    $this->sendSmsNotification($event);
+                    break;
+                case 'webhook':
+                    $this->sendWebhookNotification($event);
+                    break;
+                case 'log':
+                    $this->sendLogNotification($event);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 发送监控告警通知
+     *
+     * @param MonitorAlertEvent $event
+     * @return void
+     */
+    protected function sendMonitorAlertNotification(MonitorAlertEvent $event): void
+    {
+        $channels = $this->getNotificationChannels($event->alertLevel);
+        
+        foreach ($channels as $channel) {
+            switch ($channel) {
+                case 'email':
+                    $this->sendEmailNotification($event);
+                    break;
+                case 'sms':
+                    $this->sendSmsNotification($event);
+                    break;
+                case 'webhook':
+                    $this->sendWebhookNotification($event);
+                    break;
+                case 'log':
+                    $this->sendLogNotification($event);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 发送服务状态变更通知
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function sendServiceStatusNotification(ServiceStatusChangedEvent $event): void
+    {
+        $channels = $this->getNotificationChannels($event->getAlertLevel());
+        
+        foreach ($channels as $channel) {
+            switch ($channel) {
+                case 'email':
+                    $this->sendEmailNotification($event);
+                    break;
+                case 'sms':
+                    $this->sendSmsNotification($event);
+                    break;
+                case 'webhook':
+                    $this->sendWebhookNotification($event);
+                    break;
+                case 'log':
+                    $this->sendLogNotification($event);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 获取通知渠道
+     *
+     * @param string $alertLevel
+     * @return array
+     */
+    protected function getNotificationChannels(string $alertLevel): array
+    {
+        $config = config('thirdparty.alerts.channels', []);
+        
+        return match ($alertLevel) {
+            'CRITICAL' => $config['critical'] ?? ['email', 'sms', 'webhook', 'log'],
+            'WARNING' => $config['warning'] ?? ['email', 'log'],
+            'INFO' => $config['info'] ?? ['log'],
+            default => ['log'],
+        };
+    }
+
+    /**
+     * 发送邮件通知
+     *
+     * @param mixed $event
+     * @return void
+     */
+    protected function sendEmailNotification($event): void
+    {
+        $recipients = config('thirdparty.alerts.email.recipients', []);
+        
+        if (empty($recipients)) {
+            return;
+        }
+
+        $subject = $this->getEmailSubject($event);
+        $content = $this->getEmailContent($event);
+
+        foreach ($recipients as $recipient) {
+            try {
+                Mail::raw($content, function ($message) use ($recipient, $subject) {
+                    $message->to($recipient)
+                           ->subject($subject);
+                });
+            } catch (\Exception $e) {
+                Log::error("邮件通知发送失败: {$recipient}", [
+                    'error' => $e->getMessage(),
+                ]);
+            }
+        }
+    }
+
+    /**
+     * 发送短信通知
+     *
+     * @param mixed $event
+     * @return void
+     */
+    protected function sendSmsNotification($event): void
+    {
+        $phones = config('thirdparty.alerts.sms.phones', []);
+        
+        if (empty($phones)) {
+            return;
+        }
+
+        $message = $this->getSmsMessage($event);
+
+        foreach ($phones as $phone) {
+            try {
+                // 这里可以集成短信服务
+                Log::info("短信通知: {$phone}", ['message' => $message]);
+            } catch (\Exception $e) {
+                Log::error("短信通知发送失败: {$phone}", [
+                    'error' => $e->getMessage(),
+                ]);
+            }
+        }
+    }
+
+    /**
+     * 发送Webhook通知
+     *
+     * @param mixed $event
+     * @return void
+     */
+    protected function sendWebhookNotification($event): void
+    {
+        $webhooks = config('thirdparty.alerts.webhooks', []);
+        
+        if (empty($webhooks)) {
+            return;
+        }
+
+        $payload = $this->getWebhookPayload($event);
+
+        foreach ($webhooks as $webhook) {
+            try {
+                $response = Http::timeout(10)->post($webhook['url'], $payload);
+                
+                if (!$response->successful()) {
+                    Log::warning("Webhook通知失败: {$webhook['url']}", [
+                        'status' => $response->status(),
+                        'response' => $response->body(),
+                    ]);
+                }
+            } catch (\Exception $e) {
+                Log::error("Webhook通知发送失败: {$webhook['url']}", [
+                    'error' => $e->getMessage(),
+                ]);
+            }
+        }
+    }
+
+    /**
+     * 发送日志通知
+     *
+     * @param mixed $event
+     * @return void
+     */
+    protected function sendLogNotification($event): void
+    {
+        $level = $this->getLogLevel($event);
+        $message = $this->getLogMessage($event);
+        $context = method_exists($event, 'toArray') ? $event->toArray() : [];
+
+        Log::log($level, $message, $context);
+    }
+
+    /**
+     * 获取邮件主题
+     *
+     * @param mixed $event
+     * @return string
+     */
+    protected function getEmailSubject($event): string
+    {
+        return match (get_class($event)) {
+            QuotaAlertEvent::class => "第三方服务配额告警 - {$event->service->name}",
+            MonitorAlertEvent::class => "第三方服务监控告警 - {$event->service->name}",
+            ServiceStatusChangedEvent::class => "第三方服务状态变更 - {$event->service->name}",
+            default => '第三方服务告警通知',
+        };
+    }
+
+    /**
+     * 获取邮件内容
+     *
+     * @param mixed $event
+     * @return string
+     */
+    protected function getEmailContent($event): string
+    {
+        return match (get_class($event)) {
+            QuotaAlertEvent::class => $this->getQuotaAlertEmailContent($event),
+            MonitorAlertEvent::class => $this->getMonitorAlertEmailContent($event),
+            ServiceStatusChangedEvent::class => $this->getServiceStatusEmailContent($event),
+            default => '第三方服务告警通知',
+        };
+    }
+
+    /**
+     * 获取配额告警邮件内容
+     *
+     * @param QuotaAlertEvent $event
+     * @return string
+     */
+    protected function getQuotaAlertEmailContent(QuotaAlertEvent $event): string
+    {
+        $content = "第三方服务配额告警\n\n";
+        $content .= "服务名称: {$event->service->name}\n";
+        $content .= "服务代码: {$event->service->code}\n";
+        $content .= "告警类型: {$event->getAlertMessage()}\n";
+        $content .= "当前使用: {$event->currentUsage}/{$event->quotaLimit}\n";
+        $content .= "使用率: " . number_format($event->usagePercentage, 1) . "%\n";
+        $content .= "剩余配额: {$event->getRemainingQuota()}\n";
+        $content .= "告警时间: {$event->alertTime->format('Y-m-d H:i:s')}\n\n";
+
+        $actions = $event->getSuggestedActions();
+        if (!empty($actions)) {
+            $content .= "建议操作:\n";
+            foreach ($actions as $action) {
+                $content .= "- {$action}\n";
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * 获取监控告警邮件内容
+     *
+     * @param MonitorAlertEvent $event
+     * @return string
+     */
+    protected function getMonitorAlertEmailContent(MonitorAlertEvent $event): string
+    {
+        $content = "第三方服务监控告警\n\n";
+        $content .= "服务名称: {$event->service->name}\n";
+        $content .= "服务代码: {$event->service->code}\n";
+        $content .= "告警类型: {$event->getAlertTypeLabel()}\n";
+        $content .= "告警级别: {$event->alertLevel}\n";
+        $content .= "告警消息: {$event->alertMessage}\n";
+        $content .= "告警时间: {$event->alertTime->format('Y-m-d H:i:s')}\n\n";
+
+        $actions = $event->getSuggestedActions();
+        if (!empty($actions)) {
+            $content .= "建议操作:\n";
+            foreach ($actions as $action) {
+                $content .= "- {$action}\n";
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * 获取服务状态变更邮件内容
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return string
+     */
+    protected function getServiceStatusEmailContent(ServiceStatusChangedEvent $event): string
+    {
+        $content = "第三方服务状态变更通知\n\n";
+        $content .= "服务名称: {$event->service->name}\n";
+        $content .= "服务代码: {$event->service->code}\n";
+        $content .= "状态变更: {$event->oldStatus->getLabel()} -> {$event->newStatus->getLabel()}\n";
+        $content .= "变更原因: " . ($event->reason ?? '未知') . "\n";
+        $content .= "变更时间: {$event->changedAt->format('Y-m-d H:i:s')}\n";
+
+        return $content;
+    }
+
+    /**
+     * 获取短信消息
+     *
+     * @param mixed $event
+     * @return string
+     */
+    protected function getSmsMessage($event): string
+    {
+        return match (get_class($event)) {
+            QuotaAlertEvent::class => "【告警】{$event->service->name} 配额使用率 " . number_format($event->usagePercentage, 1) . "%",
+            MonitorAlertEvent::class => "【告警】{$event->service->name} {$event->getAlertTypeLabel()}异常",
+            ServiceStatusChangedEvent::class => "【通知】{$event->service->name} 状态变更为 {$event->newStatus->getLabel()}",
+            default => '第三方服务告警',
+        };
+    }
+
+    /**
+     * 获取Webhook载荷
+     *
+     * @param mixed $event
+     * @return array
+     */
+    protected function getWebhookPayload($event): array
+    {
+        $basePayload = [
+            'timestamp' => now()->toISOString(),
+            'source' => 'thirdparty-module',
+            'event_type' => get_class($event),
+        ];
+
+        if (method_exists($event, 'toArray')) {
+            $basePayload['data'] = $event->toArray();
+        }
+
+        return $basePayload;
+    }
+
+    /**
+     * 获取日志级别
+     *
+     * @param mixed $event
+     * @return string
+     */
+    protected function getLogLevel($event): string
+    {
+        return match (get_class($event)) {
+            QuotaAlertEvent::class => $event->isCritical() ? 'critical' : 'warning',
+            MonitorAlertEvent::class => strtolower($event->alertLevel),
+            ServiceStatusChangedEvent::class => $event->isCriticalChange() ? 'critical' : 'info',
+            default => 'info',
+        };
+    }
+
+    /**
+     * 获取日志消息
+     *
+     * @param mixed $event
+     * @return string
+     */
+    protected function getLogMessage($event): string
+    {
+        return match (get_class($event)) {
+            QuotaAlertEvent::class => $event->getAlertMessage(),
+            MonitorAlertEvent::class => $event->alertMessage,
+            ServiceStatusChangedEvent::class => $event->getNotificationMessage(),
+            default => '第三方服务事件通知',
+        };
+    }
+}

+ 227 - 0
app/Module/ThirdParty/Listeners/ApiCallLogListener.php

@@ -0,0 +1,227 @@
+<?php
+
+namespace App\Module\ThirdParty\Listeners;
+
+use App\Module\ThirdParty\Events\ApiCallEvent;
+use App\Module\ThirdParty\Services\LogService;
+use App\Module\ThirdParty\Enums\LOG_LEVEL;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * API调用日志监听器
+ * 
+ * 监听API调用事件并记录日志
+ */
+class ApiCallLogListener implements ShouldQueue
+{
+    use InteractsWithQueue;
+
+    /**
+     * 队列名称
+     *
+     * @var string
+     */
+    public $queue = 'thirdparty';
+
+    /**
+     * 处理事件
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    public function handle(ApiCallEvent $event): void
+    {
+        try {
+            // 记录API调用日志
+            $this->logApiCall($event);
+
+            // 如果是错误调用,记录错误日志
+            if ($event->isError()) {
+                $this->logError($event);
+            }
+
+            // 如果是慢调用,记录警告日志
+            if ($event->isSlowCall()) {
+                $this->logSlowCall($event);
+            }
+
+        } catch (\Exception $e) {
+            // 记录监听器错误,但不抛出异常避免影响主流程
+            Log::error('API调用日志监听器处理失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+                'trace' => $e->getTraceAsString(),
+            ]);
+        }
+    }
+
+    /**
+     * 记录API调用日志
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function logApiCall(ApiCallEvent $event): void
+    {
+        $logData = [
+            'service_id' => $event->service->id,
+            'credential_id' => $event->credential->id,
+            'request_id' => $event->requestId,
+            'level' => $event->getLogLevel(),
+            'method' => $event->method,
+            'url' => $event->url,
+            'headers' => $this->filterSensitiveData($event->headers),
+            'params' => $this->filterSensitiveData($event->params),
+            'response_status' => $event->statusCode,
+            'response_time' => $event->responseTime,
+            'response_body' => $this->truncateResponseBody($event->response),
+            'error_message' => $event->errorMessage,
+            'user_agent' => $event->headers['User-Agent'] ?? null,
+            'ip_address' => request()->ip(),
+        ];
+
+        LogService::create($logData);
+    }
+
+    /**
+     * 记录错误日志
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function logError(ApiCallEvent $event): void
+    {
+        $context = [
+            'service_code' => $event->service->code,
+            'service_name' => $event->service->name,
+            'request_id' => $event->requestId,
+            'method' => $event->method,
+            'url' => $event->url,
+            'status_code' => $event->statusCode,
+            'response_time' => $event->responseTime,
+            'error_message' => $event->errorMessage,
+        ];
+
+        Log::error("第三方API调用失败: {$event->service->name}", $context);
+    }
+
+    /**
+     * 记录慢调用日志
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function logSlowCall(ApiCallEvent $event): void
+    {
+        $threshold = config('thirdparty.monitoring.performance.slow_response_threshold', 5000);
+        
+        $context = [
+            'service_code' => $event->service->code,
+            'service_name' => $event->service->name,
+            'request_id' => $event->requestId,
+            'method' => $event->method,
+            'url' => $event->url,
+            'response_time' => $event->responseTime,
+            'threshold' => $threshold,
+        ];
+
+        Log::warning("第三方API调用响应缓慢: {$event->service->name}", $context);
+    }
+
+    /**
+     * 过滤敏感数据
+     *
+     * @param array $data
+     * @return array
+     */
+    protected function filterSensitiveData(array $data): array
+    {
+        $sensitiveFields = config('thirdparty.logging.sensitive_fields', [
+            'password', 'secret', 'token', 'key', 'authorization',
+            'access_token', 'refresh_token', 'api_key', 'private_key',
+        ]);
+
+        foreach ($sensitiveFields as $field) {
+            if (isset($data[$field])) {
+                $data[$field] = '***';
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * 截断响应体
+     *
+     * @param array|null $response
+     * @return array|null
+     */
+    protected function truncateResponseBody(?array $response): ?array
+    {
+        if (!$response) {
+            return null;
+        }
+
+        $maxSize = config('thirdparty.logging.max_response_body_size', 10240);
+        $responseJson = json_encode($response);
+
+        if (strlen($responseJson) > $maxSize) {
+            return [
+                '_truncated' => true,
+                '_original_size' => strlen($responseJson),
+                '_max_size' => $maxSize,
+                'data' => json_decode(substr($responseJson, 0, $maxSize), true),
+            ];
+        }
+
+        return $response;
+    }
+
+    /**
+     * 处理失败的任务
+     *
+     * @param ApiCallEvent $event
+     * @param \Throwable $exception
+     * @return void
+     */
+    public function failed(ApiCallEvent $event, \Throwable $exception): void
+    {
+        Log::error('API调用日志监听器任务失败', [
+            'event_data' => $event->toArray(),
+            'error' => $exception->getMessage(),
+            'trace' => $exception->getTraceAsString(),
+        ]);
+    }
+
+    /**
+     * 获取任务标签
+     *
+     * @return array
+     */
+    public function tags(): array
+    {
+        return ['thirdparty', 'api-call', 'logging'];
+    }
+
+    /**
+     * 获取任务重试次数
+     *
+     * @return int
+     */
+    public function retryAfter(): int
+    {
+        return 60; // 60秒后重试
+    }
+
+    /**
+     * 获取最大重试次数
+     *
+     * @return int
+     */
+    public function tries(): int
+    {
+        return 3;
+    }
+}

+ 313 - 0
app/Module/ThirdParty/Listeners/QuotaManagementListener.php

@@ -0,0 +1,313 @@
+<?php
+
+namespace App\Module\ThirdParty\Listeners;
+
+use App\Module\ThirdParty\Events\QuotaAlertEvent;
+use App\Module\ThirdParty\Events\ApiCallEvent;
+use App\Module\ThirdParty\Services\QuotaService;
+use App\Module\ThirdParty\Enums\QUOTA_TYPE;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
+
+/**
+ * 配额管理监听器
+ * 
+ * 监听API调用事件并更新配额使用情况
+ */
+class QuotaManagementListener implements ShouldQueue
+{
+    use InteractsWithQueue;
+
+    /**
+     * 队列名称
+     *
+     * @var string
+     */
+    public $queue = 'thirdparty';
+
+    /**
+     * 处理API调用事件
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    public function handle(ApiCallEvent $event): void
+    {
+        try {
+            // 更新配额使用情况
+            $this->updateQuotaUsage($event);
+
+            // 检查配额告警
+            $this->checkQuotaAlerts($event);
+
+        } catch (\Exception $e) {
+            Log::error('配额管理监听器处理失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+                'trace' => $e->getTraceAsString(),
+            ]);
+        }
+    }
+
+    /**
+     * 更新配额使用情况
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function updateQuotaUsage(ApiCallEvent $event): void
+    {
+        $service = $event->service;
+        
+        // 获取服务的所有配额配置
+        $quotas = $service->quotas()->where('is_active', true)->get();
+
+        foreach ($quotas as $quota) {
+            try {
+                // 增加使用次数
+                QuotaService::incrementUsage($quota->id);
+
+                // 记录使用详情
+                $this->recordUsageDetail($quota, $event);
+
+            } catch (\Exception $e) {
+                Log::warning("更新配额使用失败: {$quota->id}", [
+                    'error' => $e->getMessage(),
+                    'quota_id' => $quota->id,
+                    'service_id' => $service->id,
+                ]);
+            }
+        }
+    }
+
+    /**
+     * 检查配额告警
+     *
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function checkQuotaAlerts(ApiCallEvent $event): void
+    {
+        $service = $event->service;
+        $quotas = $service->quotas()->where('is_active', true)->get();
+
+        foreach ($quotas as $quota) {
+            try {
+                $usageInfo = QuotaService::getUsageInfo($quota->id);
+                
+                // 检查是否需要告警
+                $alertType = $this->determineAlertType($usageInfo);
+                
+                if ($alertType) {
+                    $this->triggerQuotaAlert($quota, $usageInfo, $alertType);
+                }
+
+            } catch (\Exception $e) {
+                Log::warning("检查配额告警失败: {$quota->id}", [
+                    'error' => $e->getMessage(),
+                    'quota_id' => $quota->id,
+                    'service_id' => $service->id,
+                ]);
+            }
+        }
+    }
+
+    /**
+     * 记录使用详情
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyQuota $quota
+     * @param ApiCallEvent $event
+     * @return void
+     */
+    protected function recordUsageDetail($quota, ApiCallEvent $event): void
+    {
+        $quotaType = QUOTA_TYPE::from($quota->quota_type);
+        $cacheKey = "thirdparty:quota:{$quota->id}:usage_detail:" . $quotaType->getCacheKey();
+        
+        $details = Cache::get($cacheKey, []);
+        $details[] = [
+            'timestamp' => now()->toISOString(),
+            'request_id' => $event->requestId,
+            'method' => $event->method,
+            'url' => $event->url,
+            'status_code' => $event->statusCode,
+            'response_time' => $event->responseTime,
+            'success' => $event->success,
+        ];
+
+        // 只保留最近的100条记录
+        if (count($details) > 100) {
+            $details = array_slice($details, -100);
+        }
+
+        $ttl = $quotaType->getCacheTTL();
+        Cache::put($cacheKey, $details, $ttl);
+    }
+
+    /**
+     * 确定告警类型
+     *
+     * @param array $usageInfo
+     * @return string|null
+     */
+    protected function determineAlertType(array $usageInfo): ?string
+    {
+        $usagePercentage = $usageInfo['usage_percentage'];
+        $warningThreshold = config('thirdparty.quota.alert_threshold.warning', 80);
+        $criticalThreshold = config('thirdparty.quota.alert_threshold.critical', 95);
+
+        // 检查是否已耗尽
+        if ($usagePercentage >= 100) {
+            return QuotaAlertEvent::ALERT_TYPE_EXHAUSTED;
+        }
+
+        // 检查是否达到严重阈值
+        if ($usagePercentage >= $criticalThreshold) {
+            return QuotaAlertEvent::ALERT_TYPE_CRITICAL;
+        }
+
+        // 检查是否达到警告阈值
+        if ($usagePercentage >= $warningThreshold) {
+            return QuotaAlertEvent::ALERT_TYPE_WARNING;
+        }
+
+        return null;
+    }
+
+    /**
+     * 触发配额告警
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyQuota $quota
+     * @param array $usageInfo
+     * @param string $alertType
+     * @return void
+     */
+    protected function triggerQuotaAlert($quota, array $usageInfo, string $alertType): void
+    {
+        // 检查告警冷却时间
+        if ($this->isInCooldown($quota, $alertType)) {
+            return;
+        }
+
+        $service = $quota->service;
+        $warningThreshold = config('thirdparty.quota.alert_threshold.warning', 80);
+        $criticalThreshold = config('thirdparty.quota.alert_threshold.critical', 95);
+
+        $alertThreshold = match ($alertType) {
+            QuotaAlertEvent::ALERT_TYPE_WARNING => $warningThreshold,
+            QuotaAlertEvent::ALERT_TYPE_CRITICAL => $criticalThreshold,
+            QuotaAlertEvent::ALERT_TYPE_EXHAUSTED => 100,
+            default => 0,
+        };
+
+        // 创建配额告警事件
+        $alertEvent = new QuotaAlertEvent(
+            $service,
+            $quota,
+            $alertType,
+            $usageInfo['current_usage'],
+            $usageInfo['quota_limit'],
+            $usageInfo['usage_percentage'],
+            $alertThreshold,
+            [
+                'window_start' => $usageInfo['window_start'],
+                'window_end' => $usageInfo['window_end'],
+                'remaining_quota' => $usageInfo['remaining_quota'],
+            ]
+        );
+
+        // 触发事件
+        event($alertEvent);
+
+        // 设置告警冷却时间
+        $this->setCooldown($quota, $alertType);
+
+        // 记录告警日志
+        Log::log(
+            $alertEvent->getAlertLevel() === 'CRITICAL' ? 'critical' : 'warning',
+            $alertEvent->getAlertMessage(),
+            $alertEvent->toArray()
+        );
+    }
+
+    /**
+     * 检查是否在告警冷却时间内
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyQuota $quota
+     * @param string $alertType
+     * @return bool
+     */
+    protected function isInCooldown($quota, string $alertType): bool
+    {
+        $cacheKey = "thirdparty:quota:{$quota->id}:alert_cooldown:{$alertType}";
+        return Cache::has($cacheKey);
+    }
+
+    /**
+     * 设置告警冷却时间
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyQuota $quota
+     * @param string $alertType
+     * @return void
+     */
+    protected function setCooldown($quota, string $alertType): void
+    {
+        $cooldownMinutes = config('thirdparty.alerts.cooldown_minutes', 60);
+        
+        // 严重告警的冷却时间更短
+        if ($alertType === QuotaAlertEvent::ALERT_TYPE_CRITICAL) {
+            $cooldownMinutes = config('thirdparty.alerts.critical_cooldown_minutes', 30);
+        }
+
+        $cacheKey = "thirdparty:quota:{$quota->id}:alert_cooldown:{$alertType}";
+        Cache::put($cacheKey, true, now()->addMinutes($cooldownMinutes));
+    }
+
+    /**
+     * 处理失败的任务
+     *
+     * @param ApiCallEvent $event
+     * @param \Throwable $exception
+     * @return void
+     */
+    public function failed(ApiCallEvent $event, \Throwable $exception): void
+    {
+        Log::error('配额管理监听器任务失败', [
+            'event_data' => $event->toArray(),
+            'error' => $exception->getMessage(),
+            'trace' => $exception->getTraceAsString(),
+        ]);
+    }
+
+    /**
+     * 获取任务标签
+     *
+     * @return array
+     */
+    public function tags(): array
+    {
+        return ['thirdparty', 'quota', 'management'];
+    }
+
+    /**
+     * 获取任务重试次数
+     *
+     * @return int
+     */
+    public function retryAfter(): int
+    {
+        return 30; // 30秒后重试
+    }
+
+    /**
+     * 获取最大重试次数
+     *
+     * @return int
+     */
+    public function tries(): int
+    {
+        return 3;
+    }
+}

+ 362 - 0
app/Module/ThirdParty/Listeners/ServiceStatusMonitorListener.php

@@ -0,0 +1,362 @@
+<?php
+
+namespace App\Module\ThirdParty\Listeners;
+
+use App\Module\ThirdParty\Events\ServiceStatusChangedEvent;
+use App\Module\ThirdParty\Services\MonitorService;
+use App\Module\ThirdParty\Enums\SERVICE_STATUS;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
+
+/**
+ * 服务状态监控监听器
+ * 
+ * 监听服务状态变更事件并执行相应的监控操作
+ */
+class ServiceStatusMonitorListener implements ShouldQueue
+{
+    use InteractsWithQueue;
+
+    /**
+     * 队列名称
+     *
+     * @var string
+     */
+    public $queue = 'thirdparty';
+
+    /**
+     * 处理事件
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    public function handle(ServiceStatusChangedEvent $event): void
+    {
+        try {
+            // 记录状态变更日志
+            $this->logStatusChange($event);
+
+            // 更新监控记录
+            $this->updateMonitorRecord($event);
+
+            // 处理特殊状态变更
+            $this->handleSpecialStatusChange($event);
+
+            // 更新缓存
+            $this->updateServiceCache($event);
+
+            // 如果是严重状态变更,触发告警
+            if ($event->isCriticalChange()) {
+                $this->triggerCriticalAlert($event);
+            }
+
+            // 如果是恢复状态变更,记录恢复信息
+            if ($event->isRecoveryChange()) {
+                $this->recordRecovery($event);
+            }
+
+        } catch (\Exception $e) {
+            Log::error('服务状态监控监听器处理失败', [
+                'error' => $e->getMessage(),
+                'event_data' => $event->toArray(),
+                'trace' => $e->getTraceAsString(),
+            ]);
+        }
+    }
+
+    /**
+     * 记录状态变更日志
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function logStatusChange(ServiceStatusChangedEvent $event): void
+    {
+        $level = $event->isCriticalChange() ? 'error' : 
+                ($event->isRecoveryChange() ? 'info' : 'warning');
+
+        Log::log($level, $event->getNotificationMessage(), [
+            'service_id' => $event->service->id,
+            'service_code' => $event->service->code,
+            'old_status' => $event->oldStatus->value,
+            'new_status' => $event->newStatus->value,
+            'reason' => $event->reason,
+            'metadata' => $event->metadata,
+        ]);
+    }
+
+    /**
+     * 更新监控记录
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function updateMonitorRecord(ServiceStatusChangedEvent $event): void
+    {
+        $monitorData = [
+            'service_id' => $event->service->id,
+            'check_type' => 'status_change',
+            'status' => $event->isCriticalChange() ? 'error' : 'success',
+            'response_time' => 0,
+            'details' => [
+                'old_status' => $event->oldStatus->value,
+                'new_status' => $event->newStatus->value,
+                'reason' => $event->reason,
+                'change_type' => $event->isCriticalChange() ? 'critical' : 
+                               ($event->isRecoveryChange() ? 'recovery' : 'normal'),
+                'metadata' => $event->metadata,
+            ],
+        ];
+
+        MonitorService::recordMonitorResult(
+            $event->service,
+            'status_change',
+            $monitorData
+        );
+    }
+
+    /**
+     * 处理特殊状态变更
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function handleSpecialStatusChange(ServiceStatusChangedEvent $event): void
+    {
+        switch ($event->newStatus) {
+            case SERVICE_STATUS::ERROR:
+                $this->handleErrorStatus($event);
+                break;
+
+            case SERVICE_STATUS::MAINTENANCE:
+                $this->handleMaintenanceStatus($event);
+                break;
+
+            case SERVICE_STATUS::EXPIRED:
+                $this->handleExpiredStatus($event);
+                break;
+
+            case SERVICE_STATUS::ACTIVE:
+                if ($event->isRecoveryChange()) {
+                    $this->handleRecoveryStatus($event);
+                }
+                break;
+        }
+    }
+
+    /**
+     * 处理错误状态
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function handleErrorStatus(ServiceStatusChangedEvent $event): void
+    {
+        // 增加健康检查频率
+        $this->increaseHealthCheckFrequency($event->service);
+
+        // 记录错误开始时间
+        Cache::put(
+            "thirdparty:service:{$event->service->id}:error_start",
+            $event->changedAt->toISOString(),
+            now()->addDays(7)
+        );
+
+        Log::error("第三方服务进入错误状态", [
+            'service_id' => $event->service->id,
+            'service_name' => $event->service->name,
+            'reason' => $event->reason,
+        ]);
+    }
+
+    /**
+     * 处理维护状态
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function handleMaintenanceStatus(ServiceStatusChangedEvent $event): void
+    {
+        // 暂停健康检查
+        $this->pauseHealthCheck($event->service);
+
+        Log::info("第三方服务进入维护状态", [
+            'service_id' => $event->service->id,
+            'service_name' => $event->service->name,
+            'reason' => $event->reason,
+        ]);
+    }
+
+    /**
+     * 处理过期状态
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function handleExpiredStatus(ServiceStatusChangedEvent $event): void
+    {
+        // 禁用服务调用
+        $this->disableServiceCalls($event->service);
+
+        Log::critical("第三方服务已过期", [
+            'service_id' => $event->service->id,
+            'service_name' => $event->service->name,
+            'reason' => $event->reason,
+        ]);
+    }
+
+    /**
+     * 处理恢复状态
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function handleRecoveryStatus(ServiceStatusChangedEvent $event): void
+    {
+        // 恢复正常健康检查频率
+        $this->restoreHealthCheckFrequency($event->service);
+
+        // 清除错误开始时间
+        Cache::forget("thirdparty:service:{$event->service->id}:error_start");
+
+        // 计算故障持续时间
+        $errorStart = Cache::get("thirdparty:service:{$event->service->id}:error_start");
+        if ($errorStart) {
+            $downtime = $event->changedAt->diffInMinutes(\Carbon\Carbon::parse($errorStart));
+            Log::info("第三方服务恢复正常", [
+                'service_id' => $event->service->id,
+                'service_name' => $event->service->name,
+                'downtime_minutes' => $downtime,
+            ]);
+        }
+    }
+
+    /**
+     * 更新服务缓存
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function updateServiceCache(ServiceStatusChangedEvent $event): void
+    {
+        $cacheKey = "thirdparty:service:{$event->service->id}:status";
+        Cache::put($cacheKey, $event->newStatus->value, now()->addHours(24));
+
+        // 更新服务列表缓存
+        Cache::forget('thirdparty:services:active');
+        Cache::forget('thirdparty:services:all');
+    }
+
+    /**
+     * 触发严重告警
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function triggerCriticalAlert(ServiceStatusChangedEvent $event): void
+    {
+        // 这里可以集成告警系统,如邮件、短信、钉钉等
+        Log::critical("第三方服务严重状态变更告警", [
+            'service_id' => $event->service->id,
+            'service_name' => $event->service->name,
+            'status_change' => "{$event->oldStatus->getLabel()} -> {$event->newStatus->getLabel()}",
+            'reason' => $event->reason,
+            'alert_level' => $event->getAlertLevel(),
+        ]);
+    }
+
+    /**
+     * 记录恢复信息
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @return void
+     */
+    protected function recordRecovery(ServiceStatusChangedEvent $event): void
+    {
+        Log::info("第三方服务状态恢复", [
+            'service_id' => $event->service->id,
+            'service_name' => $event->service->name,
+            'recovery_time' => $event->changedAt->toISOString(),
+            'previous_status' => $event->oldStatus->getLabel(),
+        ]);
+    }
+
+    /**
+     * 增加健康检查频率
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyService $service
+     * @return void
+     */
+    protected function increaseHealthCheckFrequency($service): void
+    {
+        // 将健康检查间隔减少到原来的1/3
+        $normalInterval = $service->health_check_interval ?? 300;
+        $emergencyInterval = max(60, intval($normalInterval / 3));
+        
+        Cache::put(
+            "thirdparty:service:{$service->id}:emergency_check_interval",
+            $emergencyInterval,
+            now()->addHours(24)
+        );
+    }
+
+    /**
+     * 暂停健康检查
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyService $service
+     * @return void
+     */
+    protected function pauseHealthCheck($service): void
+    {
+        Cache::put(
+            "thirdparty:service:{$service->id}:health_check_paused",
+            true,
+            now()->addDays(7)
+        );
+    }
+
+    /**
+     * 恢复正常健康检查频率
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyService $service
+     * @return void
+     */
+    protected function restoreHealthCheckFrequency($service): void
+    {
+        Cache::forget("thirdparty:service:{$service->id}:emergency_check_interval");
+        Cache::forget("thirdparty:service:{$service->id}:health_check_paused");
+    }
+
+    /**
+     * 禁用服务调用
+     *
+     * @param \App\Module\ThirdParty\Models\ThirdPartyService $service
+     * @return void
+     */
+    protected function disableServiceCalls($service): void
+    {
+        Cache::put(
+            "thirdparty:service:{$service->id}:calls_disabled",
+            true,
+            now()->addDays(30)
+        );
+    }
+
+    /**
+     * 处理失败的任务
+     *
+     * @param ServiceStatusChangedEvent $event
+     * @param \Throwable $exception
+     * @return void
+     */
+    public function failed(ServiceStatusChangedEvent $event, \Throwable $exception): void
+    {
+        Log::error('服务状态监控监听器任务失败', [
+            'event_data' => $event->toArray(),
+            'error' => $exception->getMessage(),
+            'trace' => $exception->getTraceAsString(),
+        ]);
+    }
+}

+ 17 - 11
app/Module/ThirdParty/Providers/ThirdPartyServiceProvider.php

@@ -96,7 +96,7 @@ class ThirdPartyServiceProvider extends ServiceProvider
         // 注册后台管理路由
         if (file_exists($adminRoutes = __DIR__ . '/../Routes/admin.php')) {
             Route::middleware(['web', 'admin'])
-                ->prefix(config('admin.route.prefix', 'admin'))
+                ->prefix(config('admin.route.prefix', 'admin') . '/thirdparty')
                 ->group($adminRoutes);
         }
 
@@ -135,20 +135,26 @@ class ThirdPartyServiceProvider extends ServiceProvider
     {
         // 注册事件监听器
         $events = [
-            \App\Module\ThirdParty\Events\ServiceCreatedEvent::class => [
-                \App\Module\ThirdParty\Listeners\ServiceCreatedListener::class,
+            // API调用事件监听器
+            \App\Module\ThirdParty\Events\ApiCallEvent::class => [
+                \App\Module\ThirdParty\Listeners\ApiCallLogListener::class,
+                \App\Module\ThirdParty\Listeners\QuotaManagementListener::class,
             ],
+
+            // 服务状态变更事件监听器
             \App\Module\ThirdParty\Events\ServiceStatusChangedEvent::class => [
-                \App\Module\ThirdParty\Listeners\ServiceStatusChangedListener::class,
-            ],
-            \App\Module\ThirdParty\Events\QuotaExceededEvent::class => [
-                \App\Module\ThirdParty\Listeners\QuotaExceededListener::class,
+                \App\Module\ThirdParty\Listeners\ServiceStatusMonitorListener::class,
+                \App\Module\ThirdParty\Listeners\AlertNotificationListener::class . '@handleServiceStatusChanged',
             ],
-            \App\Module\ThirdParty\Events\HealthCheckFailedEvent::class => [
-                \App\Module\ThirdParty\Listeners\HealthCheckFailedListener::class,
+
+            // 配额告警事件监听器
+            \App\Module\ThirdParty\Events\QuotaAlertEvent::class => [
+                \App\Module\ThirdParty\Listeners\AlertNotificationListener::class . '@handleQuotaAlert',
             ],
-            \App\Module\ThirdParty\Events\ApiCallFailedEvent::class => [
-                \App\Module\ThirdParty\Listeners\ApiCallFailedListener::class,
+
+            // 监控告警事件监听器
+            \App\Module\ThirdParty\Events\MonitorAlertEvent::class => [
+                \App\Module\ThirdParty\Listeners\AlertNotificationListener::class . '@handleMonitorAlert',
             ],
         ];
 

+ 228 - 0
app/Module/ThirdParty/Routes/admin.php

@@ -0,0 +1,228 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+use App\Module\ThirdParty\AdminControllers\ThirdPartyServiceController;
+use App\Module\ThirdParty\AdminControllers\ThirdPartyCredentialController;
+use App\Module\ThirdParty\AdminControllers\ThirdPartyLogController;
+use App\Module\ThirdParty\AdminControllers\ThirdPartyQuotaController;
+use App\Module\ThirdParty\AdminControllers\ThirdPartyMonitorController;
+
+/**
+ * ThirdParty模块后台管理路由
+ * 
+ * 路由前缀: /admin/thirdparty
+ */
+
+// 第三方服务管理
+Route::resource('services', ThirdPartyServiceController::class);
+
+// 第三方服务扩展路由
+Route::prefix('services')->name('services.')->group(function () {
+    // 健康检查
+    Route::get('health-check', [ThirdPartyServiceController::class, 'healthCheck'])->name('health-check');
+    Route::post('{id}/health-check', [ThirdPartyServiceController::class, 'performHealthCheck'])->name('perform-health-check');
+    
+    // 服务测试
+    Route::get('{id}/test', [ThirdPartyServiceController::class, 'testService'])->name('test');
+    Route::post('{id}/test', [ThirdPartyServiceController::class, 'performTest'])->name('perform-test');
+    
+    // 统计报告
+    Route::get('stats', [ThirdPartyServiceController::class, 'stats'])->name('stats');
+    
+    // 批量操作
+    Route::post('batch-activate', [ThirdPartyServiceController::class, 'batchActivate'])->name('batch-activate');
+    Route::post('batch-deactivate', [ThirdPartyServiceController::class, 'batchDeactivate'])->name('batch-deactivate');
+    
+    // 导入导出
+    Route::get('export', [ThirdPartyServiceController::class, 'export'])->name('export');
+    Route::post('import', [ThirdPartyServiceController::class, 'import'])->name('import');
+});
+
+// 认证凭证管理
+Route::resource('credentials', ThirdPartyCredentialController::class);
+
+// 认证凭证扩展路由
+Route::prefix('credentials')->name('credentials.')->group(function () {
+    // 过期凭证
+    Route::get('expired', [ThirdPartyCredentialController::class, 'expired'])->name('expired');
+    
+    // 凭证测试
+    Route::get('test', [ThirdPartyCredentialController::class, 'testCredentials'])->name('test');
+    Route::get('{id}/test', [ThirdPartyCredentialController::class, 'testCredential'])->name('test-single');
+    Route::post('{id}/test', [ThirdPartyCredentialController::class, 'performTest'])->name('perform-test');
+    
+    // 使用统计
+    Route::get('{id}/usage', [ThirdPartyCredentialController::class, 'usage'])->name('usage');
+    
+    // 批量操作
+    Route::post('batch-activate', [ThirdPartyCredentialController::class, 'batchActivate'])->name('batch-activate');
+    Route::post('batch-deactivate', [ThirdPartyCredentialController::class, 'batchDeactivate'])->name('batch-deactivate');
+    Route::post('batch-delete-expired', [ThirdPartyCredentialController::class, 'batchDeleteExpired'])->name('batch-delete-expired');
+    
+    // 凭证轮换
+    Route::post('{id}/rotate', [ThirdPartyCredentialController::class, 'rotate'])->name('rotate');
+    
+    // 导入导出
+    Route::get('export', [ThirdPartyCredentialController::class, 'export'])->name('export');
+    Route::post('import', [ThirdPartyCredentialController::class, 'import'])->name('import');
+});
+
+// 调用日志管理
+Route::resource('logs', ThirdPartyLogController::class)->except(['create', 'store', 'edit', 'update']);
+
+// 调用日志扩展路由
+Route::prefix('logs')->name('logs.')->group(function () {
+    // 统计分析
+    Route::get('stats', [ThirdPartyLogController::class, 'stats'])->name('stats');
+    
+    // 导出日志
+    Route::get('export', [ThirdPartyLogController::class, 'export'])->name('export');
+    Route::post('export', [ThirdPartyLogController::class, 'performExport'])->name('perform-export');
+    
+    // 清理日志
+    Route::get('cleanup', [ThirdPartyLogController::class, 'cleanup'])->name('cleanup');
+    Route::post('cleanup', [ThirdPartyLogController::class, 'performCleanup'])->name('perform-cleanup');
+    
+    // 重试失败请求
+    Route::post('{id}/retry', [ThirdPartyLogController::class, 'retry'])->name('retry');
+    
+    // 批量操作
+    Route::post('batch-delete', [ThirdPartyLogController::class, 'batchDelete'])->name('batch-delete');
+    
+    // 实时日志
+    Route::get('realtime', [ThirdPartyLogController::class, 'realtime'])->name('realtime');
+    
+    // 错误分析
+    Route::get('errors', [ThirdPartyLogController::class, 'errors'])->name('errors');
+    Route::get('errors/analysis', [ThirdPartyLogController::class, 'errorAnalysis'])->name('error-analysis');
+});
+
+// 配额管理
+Route::resource('quotas', ThirdPartyQuotaController::class);
+
+// 配额管理扩展路由
+Route::prefix('quotas')->name('quotas.')->group(function () {
+    // 超限配额
+    Route::get('exceeded', [ThirdPartyQuotaController::class, 'exceeded'])->name('exceeded');
+    
+    // 配额重置
+    Route::get('reset-all', [ThirdPartyQuotaController::class, 'resetAll'])->name('reset-all');
+    Route::post('reset-all', [ThirdPartyQuotaController::class, 'performResetAll'])->name('perform-reset-all');
+    Route::post('{id}/reset', [ThirdPartyQuotaController::class, 'reset'])->name('reset');
+    
+    // 增加配额
+    Route::get('{id}/increase', [ThirdPartyQuotaController::class, 'increase'])->name('increase');
+    Route::post('{id}/increase', [ThirdPartyQuotaController::class, 'performIncrease'])->name('perform-increase');
+    
+    // 使用详情
+    Route::get('{id}/usage', [ThirdPartyQuotaController::class, 'usage'])->name('usage');
+    
+    // 使用统计
+    Route::get('stats', [ThirdPartyQuotaController::class, 'stats'])->name('stats');
+    
+    // 批量操作
+    Route::post('batch-reset', [ThirdPartyQuotaController::class, 'batchReset'])->name('batch-reset');
+    Route::post('batch-activate', [ThirdPartyQuotaController::class, 'batchActivate'])->name('batch-activate');
+    Route::post('batch-deactivate', [ThirdPartyQuotaController::class, 'batchDeactivate'])->name('batch-deactivate');
+    
+    // 告警设置
+    Route::get('alerts', [ThirdPartyQuotaController::class, 'alerts'])->name('alerts');
+    Route::post('alerts', [ThirdPartyQuotaController::class, 'updateAlerts'])->name('update-alerts');
+});
+
+// 监控记录管理
+Route::resource('monitors', ThirdPartyMonitorController::class)->except(['create', 'store', 'edit', 'update']);
+
+// 监控记录扩展路由
+Route::prefix('monitors')->name('monitors.')->group(function () {
+    // 执行健康检查
+    Route::get('health-check', [ThirdPartyMonitorController::class, 'healthCheck'])->name('health-check');
+    Route::post('health-check', [ThirdPartyMonitorController::class, 'performHealthCheck'])->name('perform-health-check');
+    
+    // 重新检查
+    Route::post('{id}/recheck', [ThirdPartyMonitorController::class, 'recheck'])->name('recheck');
+    
+    // 监控统计
+    Route::get('stats', [ThirdPartyMonitorController::class, 'stats'])->name('stats');
+    
+    // 清理记录
+    Route::get('cleanup', [ThirdPartyMonitorController::class, 'cleanup'])->name('cleanup');
+    Route::post('cleanup', [ThirdPartyMonitorController::class, 'performCleanup'])->name('perform-cleanup');
+    
+    // 批量操作
+    Route::post('batch-delete', [ThirdPartyMonitorController::class, 'batchDelete'])->name('batch-delete');
+    
+    // 监控仪表板
+    Route::get('dashboard', [ThirdPartyMonitorController::class, 'dashboard'])->name('dashboard');
+    
+    // 告警历史
+    Route::get('alerts', [ThirdPartyMonitorController::class, 'alerts'])->name('alerts');
+    
+    // 性能趋势
+    Route::get('performance', [ThirdPartyMonitorController::class, 'performance'])->name('performance');
+    
+    // 可用性报告
+    Route::get('availability', [ThirdPartyMonitorController::class, 'availability'])->name('availability');
+});
+
+// 全局统计和报告
+Route::prefix('reports')->name('reports.')->group(function () {
+    // 综合报告
+    Route::get('overview', [ThirdPartyServiceController::class, 'overview'])->name('overview');
+    
+    // 服务健康报告
+    Route::get('health', [ThirdPartyServiceController::class, 'healthReport'])->name('health');
+    
+    // 使用统计报告
+    Route::get('usage', [ThirdPartyServiceController::class, 'usageReport'])->name('usage');
+    
+    // 错误分析报告
+    Route::get('errors', [ThirdPartyLogController::class, 'errorReport'])->name('errors');
+    
+    // 性能分析报告
+    Route::get('performance', [ThirdPartyMonitorController::class, 'performanceReport'])->name('performance');
+    
+    // 配额使用报告
+    Route::get('quota', [ThirdPartyQuotaController::class, 'quotaReport'])->name('quota');
+});
+
+// 系统管理
+Route::prefix('system')->name('system.')->group(function () {
+    // 配置管理
+    Route::get('config', [ThirdPartyServiceController::class, 'config'])->name('config');
+    Route::post('config', [ThirdPartyServiceController::class, 'updateConfig'])->name('update-config');
+    
+    // 缓存管理
+    Route::get('cache', [ThirdPartyServiceController::class, 'cache'])->name('cache');
+    Route::post('cache/clear', [ThirdPartyServiceController::class, 'clearCache'])->name('clear-cache');
+    
+    // 队列管理
+    Route::get('queue', [ThirdPartyServiceController::class, 'queue'])->name('queue');
+    Route::post('queue/restart', [ThirdPartyServiceController::class, 'restartQueue'])->name('restart-queue');
+    
+    // 日志管理
+    Route::get('logs', [ThirdPartyServiceController::class, 'systemLogs'])->name('logs');
+    
+    // 备份恢复
+    Route::get('backup', [ThirdPartyServiceController::class, 'backup'])->name('backup');
+    Route::post('backup', [ThirdPartyServiceController::class, 'performBackup'])->name('perform-backup');
+    Route::post('restore', [ThirdPartyServiceController::class, 'restore'])->name('restore');
+});
+
+// API接口(用于AJAX调用)
+Route::prefix('api')->name('api.')->group(function () {
+    // 服务状态检查
+    Route::get('services/{id}/status', [ThirdPartyServiceController::class, 'getStatus'])->name('service-status');
+    
+    // 实时统计数据
+    Route::get('stats/realtime', [ThirdPartyServiceController::class, 'realtimeStats'])->name('realtime-stats');
+    
+    // 配额使用情况
+    Route::get('quotas/{id}/usage', [ThirdPartyQuotaController::class, 'getUsage'])->name('quota-usage');
+    
+    // 监控数据
+    Route::get('monitors/latest', [ThirdPartyMonitorController::class, 'getLatest'])->name('monitor-latest');
+    
+    // 日志统计
+    Route::get('logs/stats', [ThirdPartyLogController::class, 'getStats'])->name('log-stats');
+});

+ 1 - 1
app/Module/ThirdParty/Services/MonitorService.php

@@ -160,7 +160,7 @@ class MonitorService
      * @param array $result
      * @return ThirdPartyMonitor
      */
-    protected static function recordMonitorResult(ThirdPartyService $service, string $checkType, array $result): ThirdPartyMonitor
+    public static function recordMonitorResult(ThirdPartyService $service, string $checkType, array $result): ThirdPartyMonitor
     {
         return ThirdPartyMonitor::createMonitor([
             'service_id' => $service->id,

+ 3 - 0
config/app.php

@@ -220,6 +220,9 @@ return [
         // OpenAPI 模块
         \App\Module\OpenAPI\Providers\OpenAPIServiceProvider::class,
 
+        // ThirdParty 模块
+        \App\Module\ThirdParty\Providers\ThirdPartyServiceProvider::class,
+
         // SQL日志服务提供者
         App\Providers\SqlLogServiceProvider::class,
     ],