Просмотр исходного кода

feat(utils): 添加智能枚举验证器

- 新增 EnumUtil 类,实现自动发现并验证请求参数中的枚举字段
- 提供
shichen месяцев назад: 11
Родитель
Сommit
61d635ba1d
1 измененных файлов с 165 добавлено и 0 удалено
  1. 165 0
      app/utils/EnumUtil.php

+ 165 - 0
app/utils/EnumUtil.php

@@ -0,0 +1,165 @@
1
+<?php
2
+
3
+namespace app\utils;
4
+
5
+use ReflectionClass;
6
+
7
+/**
8
+ * 智能枚举验证器
9
+ * 自动发现并验证请求参数中的枚举字段
10
+ */
11
+class EnumUtil
12
+{
13
+    /**
14
+     * 自动验证请求参数中的枚举字段
15
+     *
16
+     * @param string|object $enumClassOrInstance 枚举类名或实例
17
+     * @param array $requestData 请求数据
18
+     * @return array 验证结果 ['valid' => bool, 'errors' => array]
19
+     */
20
+    public static function autoValidate($enumClassOrInstance, array $requestData): array
21
+    {
22
+        $errors = [];
23
+        
24
+        try {
25
+            $reflection = new ReflectionClass($enumClassOrInstance);
26
+            $constants = $reflection->getConstants();
27
+
28
+            foreach ($constants as $constName => $enumValues) {
29
+                if (!is_array($enumValues)) {
30
+                    continue;
31
+                }
32
+                
33
+                // 跳过MAP格式的枚举定义(包含code字段的)
34
+                if (self::isMapFormat($enumValues)) {
35
+                    continue;
36
+                }
37
+
38
+                // 将枚举常量名转换为可能的请求字段名
39
+                $fieldName = self::constToFieldName($constName);
40
+
41
+                // 检查请求数据中是否存在该字段
42
+                if (array_key_exists($fieldName, $requestData)) {
43
+                    $value = $requestData[$fieldName];
44
+                    $result = self::validateEnumValue($value, $enumValues, $constName);
45
+                    if (!$result['valid']) {
46
+                        $errors[] = $result['error'];
47
+                    }
48
+                }
49
+            }
50
+
51
+        } catch (\ReflectionException $e) {
52
+            return [
53
+                'valid' => false,
54
+                'errors' => ["枚举验证异常: " . $e->getMessage()]
55
+            ];
56
+        }
57
+
58
+        if (!empty($errors)) {
59
+            return [
60
+                'valid' => false,
61
+                'errors' => $errors
62
+            ];
63
+        }
64
+
65
+        return [
66
+            'valid' => true,
67
+            'errors' => []
68
+        ];
69
+    }
70
+
71
+    /**
72
+     * 将枚举常量名转换为字段名
73
+     *
74
+     * @param string $constName 常量名称
75
+     * @return string 字段名
76
+     */
77
+    private static function constToFieldName(string $constName): string
78
+    {
79
+        // 将下划线命名转换为蛇形命名(小写+下划线)
80
+        return strtolower($constName);
81
+    }
82
+
83
+    /**
84
+     * 验证单个枚举值
85
+     *
86
+     * @param mixed $value 要验证的值
87
+     * @param array $enumValues 枚举值数组
88
+     * @param string $constName 常量名称
89
+     * @return array 验证结果 ['valid' => bool, 'error' => string|null]
90
+     */
91
+    private static function validateEnumValue($value, array $enumValues, string $constName): array
92
+    {
93
+        if (!is_array($enumValues) || empty($enumValues)) {
94
+            return [
95
+                'valid' => false,
96
+                'error' => '枚举值配置错误'
97
+            ];
98
+        }
99
+
100
+        // 提取所有有效值(简单格式的键)
101
+        $validValues = array_keys($enumValues);
102
+
103
+        // 处理字符串数字和整数的类型转换
104
+        $normalizedValue = is_numeric($value) ? (int)$value : $value;
105
+        
106
+        if (!in_array($normalizedValue, $validValues, true)) {
107
+            $fieldName = self::constToFieldName($constName);
108
+            return [
109
+                'valid' => false,
110
+                'error' => "{$fieldName}参数错误,值:{$value}"
111
+            ];
112
+        }
113
+
114
+        return [
115
+            'valid' => true,
116
+            'error' => null
117
+        ];
118
+    }
119
+
120
+    /**
121
+     * 获取枚举类的所有字段映射
122
+     *
123
+     * @param string|object $enumClassOrInstance 枚举类名或实例
124
+     * @return array 字段映射 [字段名 => 枚举值数组]
125
+     */
126
+    public static function getFieldMappings($enumClassOrInstance): array
127
+    {
128
+        $mappings = [];
129
+        
130
+        try {
131
+            $reflection = new ReflectionClass($enumClassOrInstance);
132
+            $constants = $reflection->getConstants();
133
+
134
+            foreach ($constants as $constName => $enumValues) {
135
+                if (is_array($enumValues) && !self::isMapFormat($enumValues)) {
136
+                    $fieldName = self::constToFieldName($constName);
137
+                    $mappings[$fieldName] = $enumValues;
138
+                }
139
+            }
140
+
141
+        } catch (\ReflectionException $e) {
142
+            // 忽略异常,返回空数组
143
+        }
144
+
145
+        return $mappings;
146
+    }
147
+
148
+    /**
149
+     * 检查枚举值数组是否为MAP格式(包含code字段)
150
+     *
151
+     * @param array $enumValues 枚举值数组
152
+     * @return bool
153
+     */
154
+    private static function isMapFormat(array $enumValues): bool
155
+    {
156
+        // 如果是空数组,跳过
157
+        if (empty($enumValues)) {
158
+            return false;
159
+        }
160
+        
161
+        // 检查第一个元素是否包含code字段
162
+        $firstValue = reset($enumValues);
163
+        return is_array($firstValue) && isset($firstValue['code']);
164
+    }
165
+}