| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- <?php
- namespace App\Module\OpenAPI\Validators;
- use UCore\Validator;
- /**
- * Webhook事件验证器
- */
- class WebhookEventsValidator extends Validator
- {
- /**
- * 验证Webhook事件列表是否有效
- *
- * @param mixed $value 事件列表数组
- * @param array $data 验证数据
- * @return bool 验证是否通过
- */
- public function validate(mixed $value, array $data): bool
- {
- // 从 args 获取参数
- $processedKey = $this->args[0] ?? 'processedEvents';
- if (empty($value)) {
- $this->addError('事件类型不能为空');
- return false;
- }
- if (!is_array($value)) {
- $this->addError('事件类型必须是数组格式');
- return false;
- }
- // 验证事件数量
- if (count($value) > 50) {
- $this->addError('事件类型数量不能超过50个');
- return false;
- }
- $processedEvents = [];
- $validEvents = $this->getValidEvents();
- foreach ($value as $index => $event) {
- if (!is_string($event)) {
- $this->addError("第" . ($index + 1) . "个事件类型必须是字符串");
- return false;
- }
- $event = trim($event);
- if (empty($event)) {
- continue;
- }
- // 验证事件格式
- if (!$this->validateEventFormat($event)) {
- return false;
- }
- // 验证事件是否有效
- if (!$this->isValidEvent($event, $validEvents)) {
- $this->addError("无效的事件类型: {$event}");
- return false;
- }
- $processedEvents[] = $event;
- }
- // 去重
- $processedEvents = array_unique($processedEvents);
- if (empty($processedEvents)) {
- $this->addError('至少需要配置一个事件类型');
- return false;
- }
- // 验证事件组合的合理性
- if (!$this->validateEventCombination($processedEvents)) {
- return false;
- }
- // 将处理后的事件列表保存到验证对象中
- $this->validation->$processedKey = array_values($processedEvents);
- return true;
- }
- /**
- * 获取有效的事件类型
- *
- * @return array
- */
- protected function getValidEvents(): array
- {
- return [
- // 通配符事件
- '*',
-
- // 用户事件
- 'user.created',
- 'user.updated',
- 'user.deleted',
- 'user.login',
- 'user.logout',
- 'user.password_changed',
-
- // 游戏事件
- 'game.started',
- 'game.finished',
- 'game.paused',
- 'game.resumed',
- 'game.level_up',
- 'game.achievement_unlocked',
-
- // 物品事件
- 'item.created',
- 'item.updated',
- 'item.deleted',
- 'item.transferred',
- 'item.used',
- 'item.expired',
-
- // 资金事件
- 'fund.deposited',
- 'fund.withdrawn',
- 'fund.transferred',
- 'fund.frozen',
- 'fund.unfrozen',
- 'fund.balance_changed',
-
- // 交易事件
- 'trade.created',
- 'trade.completed',
- 'trade.cancelled',
- 'trade.expired',
- 'trade.disputed',
-
- // 系统事件
- 'system.maintenance',
- 'system.upgrade',
- 'system.alert',
- 'system.backup',
-
- // API事件
- 'api.rate_limit_exceeded',
- 'api.authentication_failed',
- 'api.permission_denied',
-
- // 应用事件
- 'app.created',
- 'app.updated',
- 'app.suspended',
- 'app.activated',
- ];
- }
- /**
- * 验证事件格式
- *
- * @param string $event
- * @return bool
- */
- protected function validateEventFormat(string $event): bool
- {
- // 通配符事件
- if ($event === '*') {
- return true;
- }
- // 事件格式:category.action
- if (!preg_match('/^[a-z_]+\.[a-z_]+$/', $event)) {
- $this->addError("事件格式错误: {$event},应为 category.action 格式");
- return false;
- }
- // 验证长度
- if (strlen($event) > 50) {
- $this->addError("事件名称过长: {$event}");
- return false;
- }
- return true;
- }
- /**
- * 检查事件是否有效
- *
- * @param string $event
- * @param array $validEvents
- * @return bool
- */
- protected function isValidEvent(string $event, array $validEvents): bool
- {
- // 直接匹配
- if (in_array($event, $validEvents)) {
- return true;
- }
- // 模式匹配(支持通配符)
- foreach ($validEvents as $validEvent) {
- if ($this->matchEventPattern($event, $validEvent)) {
- return true;
- }
- }
- return false;
- }
- /**
- * 事件模式匹配
- *
- * @param string $event
- * @param string $pattern
- * @return bool
- */
- protected function matchEventPattern(string $event, string $pattern): bool
- {
- // 精确匹配
- if ($event === $pattern) {
- return true;
- }
- // 通配符匹配
- if ($pattern === '*') {
- return true;
- }
- // 分类通配符匹配(如 user.*)
- if (str_ends_with($pattern, '.*')) {
- $category = substr($pattern, 0, -2);
- return str_starts_with($event, $category . '.');
- }
- return false;
- }
- /**
- * 验证事件组合的合理性
- *
- * @param array $events
- * @return bool
- */
- protected function validateEventCombination(array $events): bool
- {
- // 如果包含通配符,警告可能的性能问题
- if (in_array('*', $events)) {
- $this->addWarning('使用通配符事件(*)会接收所有事件,可能影响性能');
-
- // 如果有通配符,其他事件就没有意义了
- if (count($events) > 1) {
- $this->addWarning('已配置通配符事件(*),其他事件配置将被忽略');
- }
- }
- // 检查是否有冲突的事件组合
- $conflictGroups = [
- ['user.created', 'user.deleted'],
- ['game.started', 'game.finished'],
- ['fund.deposited', 'fund.withdrawn'],
- ];
- foreach ($conflictGroups as $group) {
- $foundEvents = array_intersect($events, $group);
- if (count($foundEvents) === count($group)) {
- $this->addWarning('配置了相互冲突的事件: ' . implode(', ', $foundEvents));
- }
- }
- return true;
- }
- }
|