schema-dsl 1.0.9 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +325 -2
  2. package/README.md +419 -189
  3. package/STATUS.md +65 -3
  4. package/docs/FEATURE-INDEX.md +1 -1
  5. package/docs/best-practices.md +3 -3
  6. package/docs/cache-manager.md +1 -1
  7. package/docs/conditional-api.md +1278 -0
  8. package/docs/dsl-syntax.md +1 -1
  9. package/docs/dynamic-locale.md +2 -2
  10. package/docs/error-handling.md +2 -2
  11. package/docs/export-guide.md +2 -2
  12. package/docs/export-limitations.md +3 -3
  13. package/docs/faq.md +6 -6
  14. package/docs/frontend-i18n-guide.md +1 -1
  15. package/docs/mongodb-exporter.md +3 -3
  16. package/docs/multi-type-support.md +12 -2
  17. package/docs/mysql-exporter.md +1 -1
  18. package/docs/plugin-system.md +4 -4
  19. package/docs/postgresql-exporter.md +1 -1
  20. package/docs/quick-start.md +4 -4
  21. package/docs/troubleshooting.md +2 -2
  22. package/docs/type-reference.md +5 -5
  23. package/docs/typescript-guide.md +5 -6
  24. package/docs/union-type-guide.md +147 -0
  25. package/docs/union-types.md +277 -0
  26. package/docs/validate-async.md +1 -1
  27. package/examples/array-dsl-example.js +1 -1
  28. package/examples/conditional-example.js +288 -0
  29. package/examples/conditional-non-object.js +129 -0
  30. package/examples/conditional-validate-example.js +321 -0
  31. package/examples/i18n-error.examples.js +181 -0
  32. package/examples/union-type-example.js +127 -0
  33. package/examples/union-types-example.js +77 -0
  34. package/index.d.ts +655 -7
  35. package/index.js +54 -3
  36. package/lib/adapters/DslAdapter.js +14 -5
  37. package/lib/core/ConditionalBuilder.js +503 -0
  38. package/lib/core/DslBuilder.js +113 -0
  39. package/lib/core/Locale.js +13 -8
  40. package/lib/core/Validator.js +250 -2
  41. package/lib/errors/I18nError.js +222 -0
  42. package/lib/locales/en-US.js +39 -0
  43. package/lib/locales/es-ES.js +4 -0
  44. package/lib/locales/fr-FR.js +4 -0
  45. package/lib/locales/ja-JP.js +9 -0
  46. package/lib/locales/zh-CN.js +39 -0
  47. package/package.json +3 -1
@@ -649,5 +649,5 @@ dsl.match('vipLevel', { gold: 'number:0-50', silver: 'number:0-20' })
649
649
  ---
650
650
 
651
651
  **最后更新**: 2025-12-29
652
- **作者**: SchemaIO Team
652
+ **作者**: SchemaI-DSL Team
653
653
 
@@ -18,7 +18,7 @@
18
18
 
19
19
  ## 基本原理
20
20
 
21
- SchemaIO 的 `Validator` 支持在验证时动态指定语言,无需全局切换。
21
+ SchemaI-DSL 的 `Validator` 支持在验证时动态指定语言,无需全局切换。
22
22
 
23
23
  ### 核心方法
24
24
 
@@ -593,6 +593,6 @@ const schema = dsl({
593
593
  ---
594
594
 
595
595
  **最后更新**: 2025-12-25
596
- **作者**: SchemaIO Team
596
+ **作者**: SchemaI-DSL Team
597
597
 
598
598
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  ### 基础结构
25
25
 
26
- SchemaIO 验证返回的错误对象结构:
26
+ SchemaI-DSL 验证返回的错误对象结构:
27
27
 
28
28
  ```javascript
29
29
  const { dsl, validate } = require('schema-dsl');
@@ -165,7 +165,7 @@ Locale.setMessages({
165
165
 
166
166
  ### 内置错误码(简化版)
167
167
 
168
- SchemaIO 对 ajv 的错误关键字进行了简化映射,使其更易用:
168
+ SchemaI-DSL 对 ajv 的错误关键字进行了简化映射,使其更易用:
169
169
 
170
170
  #### 字符串错误码
171
171
 
@@ -3,7 +3,7 @@
3
3
  > **用途**: Schema 到数据库 DDL 的完整导出指南
4
4
  > **阅读时间**: 10分钟
5
5
 
6
- > ⚠️ **重要提示**: 并非所有 SchemaIO 特性都能导出到数据库。请先阅读 [导出限制说明](export-limitations.md) 了解哪些特性不支持导出。
6
+ > ⚠️ **重要提示**: 并非所有 SchemaI-DSL 特性都能导出到数据库。请先阅读 [导出限制说明](export-limitations.md) 了解哪些特性不支持导出。
7
7
 
8
8
  ---
9
9
 
@@ -21,7 +21,7 @@
21
21
 
22
22
  ## 概述
23
23
 
24
- SchemaIO 支持将 JSON Schema 导出为多种数据库的 DDL 语句,实现"一次定义,多处使用"。
24
+ SchemaI-DSL 支持将 JSON Schema 导出为多种数据库的 DDL 语句,实现"一次定义,多处使用"。
25
25
 
26
26
  ### 支持的数据库
27
27
 
@@ -49,7 +49,7 @@ const schema = dsl({
49
49
 
50
50
  **替代方案**:
51
51
  - 导出为最宽松的类型(`VARCHAR(255)`)
52
- - 验证逻辑保留在应用层(使用 SchemaIO 验证器)
52
+ - 验证逻辑保留在应用层(使用 SchemaI-DSL 验证器)
53
53
 
54
54
  ---
55
55
 
@@ -331,7 +331,7 @@ const schema = dsl('string!')
331
331
 
332
332
  ```
333
333
  ┌─────────────────────────────────────────┐
334
- │ 应用层(SchemaIO 完整验证) │
334
+ │ 应用层(SchemaI-DSL 完整验证) │
335
335
  │ - 条件逻辑(match/if) │
336
336
  │ - 自定义验证器 │
337
337
  │ - 复杂约束(正则、范围等) │
@@ -449,7 +449,7 @@ module.exports = {
449
449
  ```markdown
450
450
  ## 数据验证说明
451
451
 
452
- ### 应用层验证(SchemaIO
452
+ ### 应用层验证(SchemaI-DSL
453
453
  - ✅ `contact` 字段根据 `contactType` 动态验证
454
454
  - ✅ 用户名正则验证(`^[a-zA-Z0-9_]+$`)
455
455
  - ✅ 自定义业务规则验证
package/docs/faq.md CHANGED
@@ -20,12 +20,12 @@
20
20
 
21
21
  ## 基础问题
22
22
 
23
- ### Q: SchemaIO 和 Joi、Yup 有什么区别?
23
+ ### Q: SchemaI-DSL 和 Joi、Yup 有什么区别?
24
24
 
25
- **A**: SchemaIO 采用 DSL 语法,更简洁:
25
+ **A**: SchemaI-DSL 采用 DSL 语法,更简洁:
26
26
 
27
27
  ```javascript
28
- // SchemaIO - 简洁
28
+ // SchemaI-DSL - 简洁
29
29
  const schema = dsl({
30
30
  username: 'string:3-32!',
31
31
  email: 'email!'
@@ -46,7 +46,7 @@ const schema = Joi.object({
46
46
 
47
47
  ---
48
48
 
49
- ### Q: 如何安装 SchemaIO
49
+ ### Q: 如何安装 SchemaI-DSL
50
50
 
51
51
  ```bash
52
52
  npm install schema-dsl
@@ -299,7 +299,7 @@ app.post('/api/users', (req, res) => {
299
299
 
300
300
  ### Q: 缓存如何工作?
301
301
 
302
- **A**: SchemaIO 内置 LRU 缓存:
302
+ **A**: SchemaI-DSL 内置 LRU 缓存:
303
303
 
304
304
  ```javascript
305
305
  const validator = new Validator({
@@ -523,7 +523,7 @@ MySQL 会生成 `COMMENT`,PostgreSQL 会生成 `COMMENT ON COLUMN`。
523
523
 
524
524
  ## TypeScript 支持
525
525
 
526
- ### Q: SchemaIO 支持 TypeScript 吗?
526
+ ### Q: SchemaI-DSL 支持 TypeScript 吗?
527
527
 
528
528
  **A**: 支持,类型定义在 `index.d.ts`:
529
529
 
@@ -250,7 +250,7 @@ Locale.addLocale('de-DE', {
250
250
  **A**: 后端返回错误消息已经是本地化的,前端无需处理。如果需要前端验证:
251
251
 
252
252
  ```javascript
253
- // 前端可以使用相同的 SchemaIO(浏览器版)
253
+ // 前端可以使用相同的 SchemaI-DSL(浏览器版)
254
254
  import { dsl, validate } from 'schema-dsl/browser';
255
255
 
256
256
  const schema = dsl({ /* ... */ });
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## 概述
21
21
 
22
- `MongoDBExporter` 将 SchemaIO 生成的 JSON Schema 转换为 MongoDB 的 `$jsonSchema` 验证格式,可直接用于创建集合时的文档验证。
22
+ `MongoDBExporter` 将 SchemaI-DSL 生成的 JSON Schema 转换为 MongoDB 的 `$jsonSchema` 验证格式,可直接用于创建集合时的文档验证。
23
23
 
24
24
  ### 核心功能
25
25
 
@@ -104,7 +104,7 @@ const mongoSchema = exporter.export(jsonSchema);
104
104
  ```
105
105
 
106
106
  **参数**:
107
- - `jsonSchema` (Object): SchemaIO 生成的 JSON Schema 对象
107
+ - `jsonSchema` (Object): SchemaI-DSL 生成的 JSON Schema 对象
108
108
 
109
109
  **返回值**:
110
110
  - `Object`: 包含 `$jsonSchema` 的 MongoDB 验证对象
@@ -272,7 +272,7 @@ async function createValidatedCollection() {
272
272
 
273
273
  ## 导出限制
274
274
 
275
- ⚠️ **重要提示**: 并非所有 SchemaIO 特性都能导出到数据库 Schema。
275
+ ⚠️ **重要提示**: 并非所有 SchemaI-DSL 特性都能导出到数据库 Schema。
276
276
 
277
277
  **不支持导出的特性**:
278
278
  - ❌ 条件验证逻辑(`dsl.match()`, `dsl.if()`)
@@ -1,8 +1,17 @@
1
1
  # 多类型支持设计说明
2
2
 
3
+ ---
4
+
5
+ ## 📖 快速导航
6
+
7
+ - **单一类型验证**(本文档)
8
+ - **[联合类型验证](./union-type-guide.md)** - 一个字段支持多种类型(使用 `.pattern()` 正则匹配)
9
+
10
+ ---
11
+
3
12
  ## 🎯 设计原理
4
13
 
5
- SchemaIO通过**类型无关的Builder模式**实现多类型支持。
14
+ schema-dsl通过**类型无关的Builder模式**实现多类型支持。
6
15
 
7
16
  ### 核心设计
8
17
 
@@ -21,6 +30,7 @@ class DslBuilder {
21
30
 
22
31
  ---
23
32
 
33
+
24
34
  ## 📊 类型支持矩阵
25
35
 
26
36
  | DSL字符串 | 解析类型 | 支持的方法 |
@@ -307,7 +317,7 @@ amount: 'number:0-10000'
307
317
 
308
318
  ## 💡 总结
309
319
 
310
- SchemaIO的多类型支持采用**类型无关Builder + 方法智能适配**设计:
320
+ SchemaI-DSL的多类型支持采用**类型无关Builder + 方法智能适配**设计:
311
321
 
312
322
  1. **统一入口**: 所有类型都通过DslBuilder
313
323
  2. **类型感知**: 方法内部检查类型兼容性
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## 概述
21
21
 
22
- `MySQLExporter` 将 SchemaIO 生成的 JSON Schema 转换为 MySQL 的 DDL 语句,包括 `CREATE TABLE` 和索引创建语句。
22
+ `MySQLExporter` 将 SchemaI-DSL 生成的 JSON Schema 转换为 MySQL 的 DDL 语句,包括 `CREATE TABLE` 和索引创建语句。
23
23
 
24
24
  ### 核心功能
25
25
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## 概述
22
22
 
23
- SchemaIO 插件系统允许你扩展核心功能,添加自定义验证器、格式化器、导出器等。
23
+ SchemaI-DSL 插件系统允许你扩展核心功能,添加自定义验证器、格式化器、导出器等。
24
24
 
25
25
  ### 特性
26
26
 
@@ -342,7 +342,7 @@ pluginManager.register({
342
342
  安装插件。
343
343
 
344
344
  **参数**:
345
- - `schema-dsl` (Object) - SchemaIO 实例
345
+ - `schema-dsl` (Object) - SchemaI-DSL 实例
346
346
  - `pluginName` (String, optional) - 插件名称
347
347
  - `options` (Object, optional) - 安装选项
348
348
 
@@ -354,7 +354,7 @@ pluginManager.register({
354
354
 
355
355
  **参数**:
356
356
  - `pluginName` (String) - 插件名称
357
- - `schema-dsl` (Object) - SchemaIO 实例
357
+ - `schema-dsl` (Object) - SchemaI-DSL 实例
358
358
 
359
359
  **返回**: `this`
360
360
 
@@ -398,7 +398,7 @@ pluginManager.register({
398
398
  清空所有插件。
399
399
 
400
400
  **参数**:
401
- - `schema-dsl` (Object) - SchemaIO 实例
401
+ - `schema-dsl` (Object) - SchemaI-DSL 实例
402
402
 
403
403
  **返回**: `this`
404
404
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## 概述
22
22
 
23
- `PostgreSQLExporter` 将 SchemaIO 生成的 JSON Schema 转换为 PostgreSQL 的 DDL 语句,支持丰富的 PostgreSQL 特性。
23
+ `PostgreSQLExporter` 将 SchemaI-DSL 生成的 JSON Schema 转换为 PostgreSQL 的 DDL 语句,支持丰富的 PostgreSQL 特性。
24
24
 
25
25
  ### 核心功能
26
26
 
@@ -413,13 +413,13 @@ uninstallStringExtensions();
413
413
 
414
414
  ### Q: 支持TypeScript吗?
415
415
 
416
- **A**: 支持!SchemaIO提供完整的TypeScript类型定义。
416
+ **A**: 支持!SchemaI-DSL提供完整的TypeScript类型定义。
417
417
 
418
418
  ---
419
419
 
420
420
  ## 🎉 恭喜!
421
421
 
422
- 你已经掌握了SchemaIO的核心用法!
422
+ 你已经掌握了SchemaI-DSL的核心用法!
423
423
 
424
424
  **核心要点**:
425
425
  1. ✅ DSL语法简洁直观
@@ -737,13 +737,13 @@ uninstallStringExtensions();
737
737
 
738
738
  ### Q: 支持TypeScript吗?
739
739
 
740
- **A**: 支持!SchemaIO提供完整的TypeScript类型定义。
740
+ **A**: 支持!SchemaI-DSL提供完整的TypeScript类型定义。
741
741
 
742
742
  ---
743
743
 
744
744
  ## 🎉 恭喜!
745
745
 
746
- 你已经掌握了SchemaIO的核心用法!
746
+ 你已经掌握了SchemaI-DSL的核心用法!
747
747
 
748
748
  **核心要点**:
749
749
  1. ✅ DSL语法简洁直观
@@ -1,6 +1,6 @@
1
1
  # 常见问题排查指南
2
2
 
3
- > **用途**: 快速解决 SchemaIO 使用中的常见问题
3
+ > **用途**: 快速解决 SchemaI-DSL 使用中的常见问题
4
4
  > **更新**: 2025-12-26
5
5
 
6
6
  ---
@@ -378,7 +378,7 @@ db.createCollection('users', {
378
378
 
379
379
  **解决方案**:
380
380
  ```javascript
381
- // SchemaIO 默认会自动安装 String 扩展
381
+ // SchemaI-DSL 默认会自动安装 String 扩展
382
382
  // 如果未生效,手动安装:
383
383
  const { installStringExtensions } = require('schema-dsl');
384
384
  installStringExtensions();
@@ -8,7 +8,7 @@
8
8
 
9
9
  ### 基本类型
10
10
 
11
- | 类型 | SchemaIO | JSON Schema | 说明 |
11
+ | 类型 | SchemaI-DSL | JSON Schema | 说明 |
12
12
  |------|----------|-------------|------|
13
13
  | 字符串 | `string` | `{ type: 'string' }` | 文本类型 |
14
14
  | 数字 | `number` | `{ type: 'number' }` | 浮点数 |
@@ -23,7 +23,7 @@
23
23
 
24
24
  ### 格式类型(基于 string)
25
25
 
26
- | 类型 | SchemaIO | JSON Schema format | 说明 |
26
+ | 类型 | SchemaI-DSL | JSON Schema format | 说明 |
27
27
  |------|----------|-------------------|------|
28
28
  | 邮箱 | `email` | `email` | 邮箱地址 |
29
29
  | URL | `url` | `uri` | 网址 |
@@ -38,7 +38,7 @@
38
38
 
39
39
  ### 特殊类型
40
40
 
41
- | 类型 | SchemaIO | JSON Schema | 说明 |
41
+ | 类型 | SchemaI-DSL | JSON Schema | 说明 |
42
42
  |------|----------|-------------|------|
43
43
  | 二进制 | `binary` | `contentEncoding: base64` | Base64编码 |
44
44
  | ObjectId | `objectId` | `pattern: ^[0-9a-fA-F]{24}$` | MongoDB ObjectId |
@@ -156,7 +156,7 @@ const schema = dsl({
156
156
 
157
157
  ### 完整对照表
158
158
 
159
- | joi | SchemaIO DSL | 说明 |
159
+ | joi | SchemaI-DSL | 说明 |
160
160
  |-----|--------------|------|
161
161
  | `Joi.string()` | `'string'` | 字符串 |
162
162
  | `Joi.string().email()` | `'email'` | 邮箱 |
@@ -206,7 +206,7 @@ const schema = dsl({
206
206
 
207
207
  ### Q2: 为什么 `integer` 不是 `number().integer()`?
208
208
 
209
- A: SchemaIO 使用 JSON Schema 标准,`integer` 是独立类型。
209
+ A: SchemaI-DSL 使用 JSON Schema 标准,`integer` 是独立类型。
210
210
 
211
211
  ### Q3: 不支持简写吗?
212
212
 
@@ -479,12 +479,11 @@ const schema = dsl({
479
479
  const schema = dsl({
480
480
  userType: dsl('string!').label('用户类型'),
481
481
 
482
- companyName: dsl('string')
483
- .when('userType', {
484
- is: 'company',
485
- then: dsl('string!').label('公司名称'), // 企业用户必填
486
- otherwise: dsl('string').label('公司名称') // 个人用户可选
487
- })
482
+ // 使用 dsl.match() 根据 userType 字段动态验证
483
+ companyName: dsl.match('userType', {
484
+ 'company': 'string!', // 企业用户必填
485
+ '_default': 'string' // 个人用户可选
486
+ })
488
487
  });
489
488
  ```
490
489
 
@@ -0,0 +1,147 @@
1
+ # 一个字段支持多种类型
2
+
3
+ > 使用 `.pattern()` 方法匹配多种格式
4
+
5
+ ---
6
+
7
+ ## 基本用法
8
+
9
+ ```javascript
10
+ const { dsl, validate } = require('schema-dsl');
11
+
12
+ // 邮箱 或 手机号
13
+ const schema = dsl({
14
+ contact: dsl('string!')
15
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
16
+ .messages({ pattern: '必须是邮箱或手机号' })
17
+ });
18
+
19
+ validate(schema, { contact: 'test@example.com' }); // ✅
20
+ validate(schema, { contact: '13800138000' }); // ✅
21
+ validate(schema, { contact: 'invalid' }); // ❌
22
+ ```
23
+
24
+ **说明**:
25
+ - 正则中使用 `|` 表示"或",括号 `()` 分组
26
+ - 使用 `.messages()` 设置错误消息,支持多语言
27
+
28
+ ---
29
+
30
+ ## 常用示例
31
+
32
+ ### 用户登录(用户名或邮箱)
33
+
34
+ ```javascript
35
+ const loginSchema = dsl({
36
+ username: dsl('string:3-32!')
37
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|[a-zA-Z0-9_]+)$/)
38
+ .messages({ pattern: '必须是邮箱或用户名' }),
39
+ password: 'string:8-32!'
40
+ });
41
+ ```
42
+
43
+ ### 联系方式(邮箱或手机号)
44
+
45
+ ```javascript
46
+ const schema = dsl({
47
+ contact: dsl('string!')
48
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
49
+ .messages({ pattern: '必须是邮箱或手机号' })
50
+ });
51
+ ```
52
+
53
+ ### URL(http 或 https)
54
+
55
+ ```javascript
56
+ const schema = dsl({
57
+ website: dsl('string!')
58
+ .pattern(/^https?:\/\/.+$/)
59
+ .messages({ pattern: '必须是 http 或 https 开头的 URL' })
60
+ });
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 支持多语言
66
+
67
+ ```javascript
68
+ // 使用多语言 key
69
+ const schema = dsl({
70
+ contact: dsl('string!')
71
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
72
+ .messages({ pattern: 'pattern.emailOrPhone' }) // 多语言 key
73
+ });
74
+
75
+ // 验证时指定语言
76
+ validate(schema, { contact: 'invalid' }, { locale: 'zh-CN' }); // 中文:必须是邮箱或手机号
77
+ validate(schema, { contact: 'invalid' }, { locale: 'en-US' }); // 英文:Must be an email or phone number
78
+ ```
79
+
80
+ **内置多语言 key**:
81
+ - `pattern.emailOrPhone` - 邮箱或手机号
82
+ - `pattern.usernameOrEmail` - 用户名或邮箱
83
+ - `pattern.httpOrHttps` - http 或 https URL
84
+
85
+ ---
86
+
87
+ ## 正则表达式速查
88
+
89
+ ```javascript
90
+ // 邮箱 或 手机号
91
+ /^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/
92
+
93
+ // http 或 https
94
+ /^https?:\/\/.+$/
95
+
96
+ // 用户名 或 邮箱
97
+ /^([^\s@]+@[^\s@]+\.[^\s@]+|[a-zA-Z0-9_]{3,32})$/
98
+
99
+ // 数字ID 或 UUID
100
+ /^(\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i
101
+
102
+ // 多个邮箱域名
103
+ /^[^\s@]+@(gmail\.com|qq\.com|163\.com)$/
104
+
105
+ // 中国或美国手机号
106
+ /^(1[3-9]\d{9}|\+1\d{10})$/
107
+ ```
108
+
109
+ ---
110
+
111
+
112
+ ## 完整示例
113
+
114
+ ```javascript
115
+ const { dsl, validate } = require('schema-dsl');
116
+
117
+ const registerSchema = dsl({
118
+ name: 'string:1-50!',
119
+ contact: dsl('string!')
120
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
121
+ .messages({ pattern: '必须是邮箱或手机号' })
122
+ });
123
+
124
+ const testData = [
125
+ { name: '张三', contact: 'zhangsan@example.com' },
126
+ { name: '李四', contact: '13800138000' },
127
+ { name: '王五', contact: 'invalid' }
128
+ ];
129
+
130
+ testData.forEach((data, index) => {
131
+ const result = validate(registerSchema, data);
132
+ console.log(`测试${index + 1}:`, result.valid ? '✅' : '❌');
133
+ if (!result.valid) {
134
+ console.log(' 错误:', result.errors[0].message);
135
+ }
136
+ });
137
+ ```
138
+
139
+ **输出**:
140
+ ```
141
+ 测试1: ✅
142
+ 测试2: ✅
143
+ 测试3: ❌
144
+ 错误: 必须是邮箱或手机号
145
+ ```
146
+
147
+