# Kuban API 验证框架 基于 buf/validate 优化设计的自定义字段验证框架,提供简化的 API、中文支持和业务规则集成。 ## 目录 - [特性](#特性) - [快速开始](#快速开始) - [核心概念](#核心概念) - [使用指南](#使用指南) - [业务规则](#业务规则) - [分组验证](#分组验证) - [最佳实践](#最佳实践) ## 特性 ### 🚀 相比 buf/validate 的优化 1. **简化的 API 设计** - 更直观的规则定义语法 - 减少冗余配置 - 更好的类型安全 2. **增强的中文支持** - 内置中文错误消息 - 中文特定验证规则(手机号、身份证等) - 双语错误消息支持 3. **业务规则集成** - 开箱即用的中国业务验证规则 - 手机号、身份证、统一社会信用代码等 - 易于扩展的业务规则库 4. **分组验证** - 场景化验证支持 - 创建、更新、审核等不同场景使用不同规则 - 灵活的规则组合 5. **保留 CEL 支持** - 完整的 CEL 表达式支持 - 灵活的自定义验证逻辑 - 跨字段验证能力 ## 快速开始 ### 1. 引入依赖 ```proto import "kuban/api/validate.proto"; import "kuban/api/business_rules.proto"; // 可选:业务规则 import "kuban/api/rule_groups.proto"; // 可选:分组验证 ``` ### 2. 基本字段验证 ```proto message User { // 必填字符串,长度 2-50 string name = 1 [(kuban.api.validate.field) = { required: true, string: { min_len: 2, max_len: 50 } }]; // 必填邮箱 string email = 2 [(kuban.api.validate.field) = { required: true, string: { email: true } }]; // 年龄范围 0-150 int32 age = 3 [(kuban.api.validate.field) = { int32: { gte: 0, lte: 150 } }]; } ``` ### 3. 中文业务验证 ```proto message ChineseUser { // 中国手机号 string mobile = 1 [(kuban.api.validate.field) = { required: true, string: { chinese_mobile: true } }]; // 中国身份证 string id_card = 2 [(kuban.api.validate.field) = { required: true, string: { chinese_id_card: true } }]; // 中文姓名 string name = 3 [(kuban.api.validate.field) = { required: true, string: { chinese_name: true } }]; } ``` ## 核心概念 ### 验证级别 框架支持三个级别的验证: 1. **字段级别验证** - 验证单个字段的值 2. **消息级别验证** - 验证整个消息的一致性(跨字段验证) 3. **Oneof 级别验证** - 验证 oneof 字段的选择 ### 验证规则类型 - **类型特定规则**: 针对不同数据类型的验证规则(如 StringRules, Int32Rules) - **通用规则**: 适用于所有类型的规则(如 required, ignore) - **CEL 自定义规则**: 使用 CEL 表达式的自定义验证逻辑 - **业务规则**: 预定义的业务场景验证规则 ## 使用指南 ### 字符串验证 ```proto message StringExample { // 长度限制 string username = 1 [(kuban.api.validate.field).string = { min_len: 3, max_len: 20 }]; // 正则表达式 string code = 2 [(kuban.api.validate.field).string = { pattern: "^[A-Z]{2}\\d{4}$" }]; // 前缀/后缀 string ref_id = 3 [(kuban.api.validate.field).string = { prefix: "REF_" }]; // 枚举值 string status = 4 [(kuban.api.validate.field).string = { in: ["pending", "approved", "rejected"] }]; // 预定义格式 string email = 5 [(kuban.api.validate.field).string.email = true]; string url = 6 [(kuban.api.validate.field).string.uri = true]; string uuid = 7 [(kuban.api.validate.field).string.uuid = true]; string ip = 8 [(kuban.api.validate.field).string.ip = true]; } ``` ### 数值验证 ```proto message NumberExample { // 范围验证 int32 score = 1 [(kuban.api.validate.field).int32 = { gte: 0, lte: 100 }]; // 常量值 int32 version = 2 [(kuban.api.validate.field).int32.const = 1]; // 枚举值 int32 priority = 3 [(kuban.api.validate.field).int32 = { in: [1, 2, 3, 4, 5] }]; // 浮点数 double price = 4 [(kuban.api.validate.field).double = { gt: 0.0, finite: true // 不允许 NaN 和 Infinity }]; } ``` ### 复杂类型验证 ```proto message ComplexExample { // 重复字段 repeated string tags = 1 [(kuban.api.validate.field).repeated = { min_items: 1, max_items: 10, unique: true, items: { string: { min_len: 2, max_len: 20 } } }]; // Map 字段 map scores = 2 [(kuban.api.validate.field).map = { min_pairs: 1, max_pairs: 100, keys: { string: { min_len: 1 } }, values: { int32: { gte: 0, lte: 100 } } }]; } ``` ### CEL 自定义验证 ```proto message CELExample { string password = 1 [(kuban.api.validate.field) = { required: true, cel: [ { id: "password.length", message: "密码长度必须在 8-32 之间", expression: "this.size() >= 8 && this.size() <= 32" }, { id: "password.complexity", message: "密码必须包含大小写字母和数字", expression: "this.matches('(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d)')" } ] }]; // 跨字段验证 int64 start_time = 2; int64 end_time = 3 [(kuban.api.validate.field).cel = [{ id: "time.range", message: "结束时间必须大于开始时间", expression: "this > this.start_time" }]]; } ``` ### 消息级别验证 ```proto message MessageExample { option (kuban.api.validate.message).cel = [ { id: "at_least_one_contact", message: "邮箱和手机号至少需要填写一个", expression: "has(this.email) || has(this.mobile)" } ]; optional string email = 1; optional string mobile = 2; } ``` ## 业务规则 ### 中国手机号验证 ```proto message MobileExample { // 简单用法 string mobile = 1 [(kuban.api.validate.field).string.chinese_mobile = true]; // 详细配置 string mobile_advanced = 2 [(kuban.api.validate.field).string = { (kuban.api.validate.chinese_mobile_rules): { enabled: true, allowed_operators: [ MOBILE_OPERATOR_CHINA_MOBILE, MOBILE_OPERATOR_CHINA_UNICOM, MOBILE_OPERATOR_CHINA_TELECOM ], allow_virtual: false, error_message: "请输入有效的中国大陆手机号" } }]; } ``` **支持的运营商:** - 中国移动 (MOBILE_OPERATOR_CHINA_MOBILE) - 中国联通 (MOBILE_OPERATOR_CHINA_UNICOM) - 中国电信 (MOBILE_OPERATOR_CHINA_TELECOM) - 中国广电 (MOBILE_OPERATOR_CHINA_BROADNET) ### 中国身份证验证 ```proto message IdCardExample { string id_card = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.chinese_id_card_rules): { enabled: true, verify_checksum: true, // 验证校验位 verify_birth_date: true, // 验证出生日期 verify_area_code: true, // 验证地区码 allowed_types: [ID_CARD_TYPE_18_DIGIT], min_age: 18, max_age: 65, required_gender: GENDER_UNSPECIFIED, // 不限制性别 error_message: "请输入有效的18位身份证号" } }]; } ``` **验证内容:** - 长度验证(15位/18位) - 校验位算法验证 - 出生日期有效性 - 地区码有效性 - 年龄范围限制 - 性别限制(可选) ### 统一社会信用代码验证 ```proto message USCCExample { string uscc = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.uscc_rules): { enabled: true, verify_checksum: true, allowed_types: [ ORGANIZATION_TYPE_ENTERPRISE, ORGANIZATION_TYPE_INSTITUTION ], error_message: "请输入有效的统一社会信用代码" } }]; } ``` ### 银行卡号验证 ```proto message BankCardExample { string bank_card = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.bank_card_rules): { enabled: true, verify_luhn: true, // Luhn 算法校验 allowed_types: [ BANK_CARD_TYPE_DEBIT, BANK_CARD_TYPE_CREDIT ], allowed_banks: ["ICBC", "CCB", "ABC"], // 允许的银行 error_message: "请输入有效的银行卡号" } }]; } ``` ### 中文姓名验证 ```proto message NameExample { string name = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.chinese_name_rules): { enabled: true, min_length: 2, max_length: 20, allow_ethnic_minority: true, // 允许少数民族姓名(如阿依·古丽) allow_english: false, error_message: "请输入2-20个字符的中文姓名" } }]; } ``` ### 邮政编码验证 ```proto message PostcodeExample { string postcode = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.postcode_rules): { enabled: true, country_code: "CN", allowed_provinces: ["11", "31", "44"], // 北京、上海、广东 error_message: "请输入有效的邮政编码" } }]; } ``` ## 分组验证 ### 基本用法 ```proto message User { // 在创建和更新时都验证 string username = 1 [(kuban.api.validate.field) = { required: true, groups: ["create", "update"], string: { min_len: 3, max_len: 20 } }]; // 仅在创建时验证 string password = 2 [(kuban.api.validate.field) = { required: true, groups: ["create"], string: { min_len: 8 } }]; // 仅在修改密码时验证 string new_password = 3 [(kuban.api.validate.field) = { required: true, groups: ["change_password"], string: { min_len: 8 } }]; } ``` ### 常用验证组 框架预定义了以下常用验证组: **CRUD 操作:** - `create` - 创建场景 - `update` - 更新场景 - `patch` - 部分更新场景 - `delete` - 删除场景 - `query` - 查询场景 **严格程度:** - `strict` - 严格模式 - `normal` - 标准模式 - `loose` - 宽松模式 **用户场景:** - `register` - 用户注册 - `login` - 用户登录 - `profile` - 个人资料 - `change_password` - 修改密码 - `reset_password` - 重置密码 **审核场景:** - `draft` - 草稿 - `submit` - 提交审核 - `approve` - 审核通过 - `reject` - 审核拒绝 **认证场景:** - `real_name_auth` - 实名认证 - `enterprise_auth` - 企业认证 - `bank_card_binding` - 银行卡绑定 **安全场景:** - `sensitive_op` - 敏感操作 - `financial` - 金融交易 - `admin` - 管理员操作 ### 验证上下文 在运行时,你可以通过 `ValidationContext` 指定激活的验证组: ```proto message ValidationContext { repeated string active_groups = 1; // ["create", "strict"] repeated string user_roles = 2; bool is_admin = 6; string language = 9; // "zh-CN" 或 "en-US" } ``` ## 忽略策略 控制何时跳过验证: ```proto message IgnoreExample { // 未设置时不验证(默认行为) optional string url = 1 [(kuban.api.validate.field) = { ignore: IGNORE_UNSPECIFIED, string: { uri: true } }]; // 为零值时不验证 optional string optional_url = 2 [(kuban.api.validate.field) = { ignore: IGNORE_IF_ZERO, string: { uri: true } }]; // 始终验证 optional string required_field = 3 [(kuban.api.validate.field) = { ignore: IGNORE_NEVER, required: true }]; // 始终忽略(用于临时禁用) string temp_field = 4 [(kuban.api.validate.field) = { ignore: IGNORE_ALWAYS, string: { min_len: 10 } }]; } ``` ## 完整示例 ### 用户注册 ```proto message UserRegistrationRequest { option (kuban.api.validate.message) = { groups: ["register"] }; string username = 1 [(kuban.api.validate.field) = { required: true, groups: ["register"], string: { min_len: 3, max_len: 20, pattern: "^[a-zA-Z0-9_]+$" }, cel: [{ id: "username.reserved", message: "该用户名为系统保留,不可使用", expression: "!['admin', 'root'].contains(this.toLowerCase())" }] }]; string password = 2 [(kuban.api.validate.field) = { required: true, groups: ["register"], string: { min_len: 8, max_len: 32 }, cel: [{ id: "password.complexity", message: "密码必须包含大小写字母、数字和特殊字符", expression: "this.matches('(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d)(?=.*[@$!%*?&])')" }] }]; string mobile = 3 [(kuban.api.validate.field) = { required: true, groups: ["register"], string: { chinese_mobile: true } }]; string verification_code = 4 [(kuban.api.validate.field) = { required: true, groups: ["register"], string: { len: 6, numeric: true } }]; optional string email = 5 [(kuban.api.validate.field) = { groups: ["register"], ignore: IGNORE_IF_ZERO, string: { email: true } }]; bool agree_terms = 6 [(kuban.api.validate.field) = { required: true, groups: ["register"], bool: { const: true } }]; } ``` ### 企业认证 ```proto message EnterpriseAuthRequest { option (kuban.api.validate.message) = { groups: ["enterprise_auth"] }; string company_name = 1 [(kuban.api.validate.field) = { required: true, groups: ["enterprise_auth"], string: { min_len: 2, max_len: 100 } }]; string uscc = 2 [(kuban.api.validate.field) = { required: true, groups: ["enterprise_auth"], string: { len: 18, (kuban.api.validate.uscc_rules): { enabled: true, verify_checksum: true, allowed_types: [ORGANIZATION_TYPE_ENTERPRISE] } } }]; string legal_person = 3 [(kuban.api.validate.field) = { required: true, groups: ["enterprise_auth"], string: { chinese_name: true, (kuban.api.validate.chinese_name_rules): { enabled: true, min_length: 2, max_length: 20 } } }]; string legal_person_id_card = 4 [(kuban.api.validate.field) = { required: true, groups: ["enterprise_auth"], string: { chinese_id_card: true, (kuban.api.validate.chinese_id_card_rules): { enabled: true, verify_checksum: true, min_age: 18 } } }]; } ``` ## 最佳实践 ### 1. 优先使用内置规则 ```proto // ✅ 好 - 使用内置规则 string email = 1 [(kuban.api.validate.field).string.email = true]; // ❌ 不推荐 - 自己写正则 string email = 2 [(kuban.api.validate.field).string = { pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" }]; ``` ### 2. 合理使用验证组 ```proto // ✅ 好 - 根据场景使用不同规则 message User { string password = 1 [(kuban.api.validate.field) = { groups: ["create", "change_password"], required: true, string: { min_len: 8 } }]; } // ❌ 不推荐 - 所有场景都验证密码 message User { string password = 1 [(kuban.api.validate.field) = { required: true, // 更新用户资料时也要求密码 string: { min_len: 8 } }]; } ``` ### 3. 提供友好的错误消息 ```proto // ✅ 好 - 提供清晰的错误消息 string mobile = 1 [(kuban.api.validate.field).string = { (kuban.api.validate.chinese_mobile_rules): { enabled: true, error_message: "请输入有效的11位手机号" } }]; // ❌ 不推荐 - 使用默认消息 string mobile = 2 [(kuban.api.validate.field).string.chinese_mobile = true]; ``` ### 4. 适当使用 CEL ```proto // ✅ 好 - 复杂逻辑使用 CEL message DateRange { option (kuban.api.validate.message).cel = [{ id: "date_range.valid", message: "结束日期必须大于开始日期", expression: "this.end_date > this.start_date" }]; int64 start_date = 1; int64 end_date = 2; } // ❌ 不推荐 - 简单验证使用 CEL string email = 1 [(kuban.api.validate.field).cel = [{ expression: "this.contains('@')" // 应该用 string.email = true }]]; ``` ### 5. 使用忽略策略优化可选字段 ```proto // ✅ 好 - 可选字段使用 IGNORE_IF_ZERO optional string website = 1 [(kuban.api.validate.field) = { ignore: IGNORE_IF_ZERO, string: { uri: true } }]; // ❌ 不推荐 - 不必要的 required optional string website = 2 [(kuban.api.validate.field) = { required: true, // optional 和 required 矛盾 string: { uri: true } }]; ``` ## 文件结构 ``` proto/kuban/api/ ├── validate.proto # 核心验证框架 ├── business_rules.proto # 业务规则库 ├── rule_groups.proto # 规则分组管理 ├── validate_example.proto # 使用示例 └── README_VALIDATE.md # 本文档 ``` ## 与 buf/validate 的对比 | 特性 | buf/validate | kuban.api.validate | |------|--------------|-------------------| | 基本验证 | ✅ | ✅ | | CEL 支持 | ✅ | ✅ | | 中文错误消息 | ❌ | ✅ | | 中国业务规则 | ❌ | ✅ (手机号、身份证等) | | 分组验证 | ❌ | ✅ | | 简化 API | ❌ | ✅ | | 双语支持 | ❌ | ✅ | | 条件验证 | 基础 | ✅ 增强 | ## 常见问题 ### Q: 如何在代码中使用这些验证规则? A: 你需要实现一个验证器来解析和执行这些规则。框架只定义了规则的结构,具体的验证逻辑需要在运行时实现。 ### Q: 可以自定义业务规则吗? A: 可以!你可以通过 extend StringRules 添加自己的业务规则扩展。 ### Q: 验证组在运行时如何激活? A: 通过 ValidationContext 消息传递激活的组名列表。 ### Q: CEL 表达式可以访问哪些变量? A: - `this` - 当前字段的值 - `this.field_name` - 同一消息的其他字段(跨字段验证) - 内置函数如 `size()`, `matches()`, `contains()` 等 ### Q: 如何处理验证错误? A: 验证器应该返回 ValidationResult 消息,包含所有的 ValidationError 详情。 ## 贡献 欢迎提交 Issue 和 Pull Request 来改进这个验证框架! ## 许可证 Copyright 2025 Kuban Technologies