engine.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. package validator
  2. import (
  3. "fmt"
  4. "google.golang.org/protobuf/proto"
  5. "google.golang.org/protobuf/reflect/protoreflect"
  6. validate "git.ikuban.com/server/kubanapis/kuban/api/validate"
  7. )
  8. // Engine 验证引擎
  9. type Engine struct {
  10. celExecutor *CELExecutor
  11. }
  12. // NewEngine 创建验证引擎
  13. func NewEngine(celExecutor *CELExecutor) *Engine {
  14. return &Engine{
  15. celExecutor: celExecutor,
  16. }
  17. }
  18. // Validate 验证消息
  19. func (e *Engine) Validate(vctx *ValidationContext, msg proto.Message) error {
  20. if msg == nil {
  21. return nil
  22. }
  23. errors := &ValidationErrors{}
  24. // 获取消息的反射对象
  25. msgReflect := msg.ProtoReflect()
  26. msgDescriptor := msgReflect.Descriptor()
  27. // 1. 验证消息级别规则
  28. if err := e.validateMessageRules(vctx, msgReflect, msgDescriptor, errors); err != nil {
  29. return err
  30. }
  31. // 2. 验证字段
  32. if err := e.validateFields(vctx, msgReflect, msgDescriptor, errors); err != nil {
  33. return err
  34. }
  35. // 3. 验证 oneof
  36. if err := e.validateOneofs(vctx, msgReflect, msgDescriptor, errors); err != nil {
  37. return err
  38. }
  39. // 返回错误(如果有)
  40. if errors.HasErrors() {
  41. return errors.ToKratosError()
  42. }
  43. return nil
  44. }
  45. // validateMessageRules 验证消息级别规则
  46. func (e *Engine) validateMessageRules(
  47. vctx *ValidationContext,
  48. msgReflect protoreflect.Message,
  49. msgDescriptor protoreflect.MessageDescriptor,
  50. errors *ValidationErrors,
  51. ) error {
  52. // 获取消息选项
  53. opts := msgDescriptor.Options()
  54. if opts == nil {
  55. return nil
  56. }
  57. // 检查是否有验证规则扩展
  58. if !proto.HasExtension(opts, validate.E_Message) {
  59. return nil
  60. }
  61. // 获取消息验证规则
  62. msgRules := proto.GetExtension(opts, validate.E_Message).(*validate.MessageRules)
  63. if msgRules == nil {
  64. return nil
  65. }
  66. // 检查是否被禁用
  67. if msgRules.Disabled {
  68. return nil
  69. }
  70. // 检查验证组
  71. if !vctx.HasAnyGroup(msgRules.Groups) {
  72. return nil
  73. }
  74. // 执行 CEL 规则
  75. for _, celRule := range msgRules.Cel {
  76. if err := e.executeCELRule(vctx, celRule, msgReflect.Interface(), "", errors); err != nil {
  77. if vctx.ShouldFailFast() {
  78. return err
  79. }
  80. }
  81. }
  82. return nil
  83. }
  84. // validateFields 验证所有字段
  85. func (e *Engine) validateFields(
  86. vctx *ValidationContext,
  87. msgReflect protoreflect.Message,
  88. msgDescriptor protoreflect.MessageDescriptor,
  89. errors *ValidationErrors,
  90. ) error {
  91. fields := msgDescriptor.Fields()
  92. for i := 0; i < fields.Len(); i++ {
  93. field := fields.Get(i)
  94. fieldPath := string(field.Name())
  95. // 检查是否达到最大错误数
  96. if vctx.MaxErrorsReached(errors.Count()) {
  97. break
  98. }
  99. // 验证字段
  100. if err := e.validateField(vctx, msgReflect, field, fieldPath, errors); err != nil {
  101. if vctx.ShouldFailFast() {
  102. return err
  103. }
  104. }
  105. }
  106. return nil
  107. }
  108. // validateField 验证单个字段
  109. func (e *Engine) validateField(
  110. vctx *ValidationContext,
  111. msgReflect protoreflect.Message,
  112. field protoreflect.FieldDescriptor,
  113. fieldPath string,
  114. errors *ValidationErrors,
  115. ) error {
  116. // 获取字段选项
  117. opts := field.Options()
  118. if opts == nil {
  119. return nil
  120. }
  121. // 检查是否有验证规则
  122. if !proto.HasExtension(opts, validate.E_Field) {
  123. return nil
  124. }
  125. // 获取字段验证规则
  126. fieldRules := proto.GetExtension(opts, validate.E_Field).(*validate.FieldRules)
  127. if fieldRules == nil {
  128. return nil
  129. }
  130. // 检查验证组
  131. if !vctx.HasAnyGroup(fieldRules.Groups) {
  132. return nil
  133. }
  134. // 获取字段值
  135. fieldValue := msgReflect.Get(field)
  136. hasValue := msgReflect.Has(field)
  137. // 检查忽略策略
  138. if e.shouldIgnoreField(fieldRules, hasValue, fieldValue, field) {
  139. return nil
  140. }
  141. // 验证 required
  142. if fieldRules.Required {
  143. if !hasValue {
  144. defaultMsg := fmt.Sprintf("字段 %s 是必填的", fieldPath)
  145. message := e.getErrorMessage(fieldRules, vctx, "required", defaultMsg)
  146. errors.Add(NewValidationError(
  147. fieldPath,
  148. ErrCodeRequired,
  149. message,
  150. ))
  151. if vctx.ShouldFailFast() {
  152. return errors
  153. }
  154. }
  155. }
  156. // 如果字段未设置且不是 required,跳过后续验证
  157. if !hasValue && !fieldRules.Required {
  158. return nil
  159. }
  160. // 执行 CEL 规则
  161. for _, celRule := range fieldRules.Cel {
  162. if err := e.executeCELFieldRule(
  163. vctx,
  164. celRule,
  165. fieldValue.Interface(),
  166. msgReflect.Interface(),
  167. fieldPath,
  168. errors,
  169. ); err != nil {
  170. if vctx.ShouldFailFast() {
  171. return err
  172. }
  173. }
  174. }
  175. // 根据字段类型执行类型特定验证
  176. return e.validateFieldType(vctx, fieldRules, fieldValue, field, fieldPath, errors)
  177. }
  178. // validateFieldType 根据字段类型执行验证
  179. func (e *Engine) validateFieldType(
  180. vctx *ValidationContext,
  181. fieldRules *validate.FieldRules,
  182. fieldValue protoreflect.Value,
  183. field protoreflect.FieldDescriptor,
  184. fieldPath string,
  185. errors *ValidationErrors,
  186. ) error {
  187. // 根据规则类型分发
  188. switch rule := fieldRules.Type.(type) {
  189. case *validate.FieldRules_String_:
  190. return e.validateString(vctx, fieldRules, rule.String_, fieldValue.String(), fieldPath, errors)
  191. case *validate.FieldRules_Int32:
  192. return e.validateInt32(vctx, fieldRules, rule.Int32, int32(fieldValue.Int()), fieldPath, errors)
  193. case *validate.FieldRules_Int64:
  194. return e.validateInt64(vctx, fieldRules, rule.Int64, fieldValue.Int(), fieldPath, errors)
  195. case *validate.FieldRules_Uint32:
  196. return e.validateUInt32(vctx, fieldRules, rule.Uint32, uint32(fieldValue.Uint()), fieldPath, errors)
  197. case *validate.FieldRules_Uint64:
  198. return e.validateUInt64(vctx, fieldRules, rule.Uint64, fieldValue.Uint(), fieldPath, errors)
  199. case *validate.FieldRules_Sint32:
  200. return e.validateInt32(vctx, fieldRules, (*validate.Int32Rules)(rule.Sint32), int32(fieldValue.Int()), fieldPath, errors)
  201. case *validate.FieldRules_Sint64:
  202. return e.validateInt64(vctx, fieldRules, (*validate.Int64Rules)(rule.Sint64), fieldValue.Int(), fieldPath, errors)
  203. case *validate.FieldRules_Fixed32:
  204. return e.validateUInt32(vctx, fieldRules, (*validate.UInt32Rules)(rule.Fixed32), uint32(fieldValue.Uint()), fieldPath, errors)
  205. case *validate.FieldRules_Fixed64:
  206. return e.validateUInt64(vctx, fieldRules, (*validate.UInt64Rules)(rule.Fixed64), fieldValue.Uint(), fieldPath, errors)
  207. case *validate.FieldRules_Sfixed32:
  208. return e.validateInt32(vctx, fieldRules, (*validate.Int32Rules)(rule.Sfixed32), int32(fieldValue.Int()), fieldPath, errors)
  209. case *validate.FieldRules_Sfixed64:
  210. return e.validateInt64(vctx, fieldRules, (*validate.Int64Rules)(rule.Sfixed64), fieldValue.Int(), fieldPath, errors)
  211. case *validate.FieldRules_Float:
  212. return e.validateFloat(vctx, fieldRules, rule.Float, float32(fieldValue.Float()), fieldPath, errors)
  213. case *validate.FieldRules_Double:
  214. return e.validateDouble(vctx, fieldRules, rule.Double, fieldValue.Float(), fieldPath, errors)
  215. case *validate.FieldRules_Bool:
  216. return e.validateBool(vctx, fieldRules, rule.Bool, fieldValue.Bool(), fieldPath, errors)
  217. case *validate.FieldRules_Bytes:
  218. return e.validateBytes(vctx, fieldRules, rule.Bytes, fieldValue.Bytes(), fieldPath, errors)
  219. case *validate.FieldRules_Enum:
  220. return e.validateEnum(vctx, fieldRules, rule.Enum, fieldValue.Enum(), field, fieldPath, errors)
  221. case *validate.FieldRules_Repeated:
  222. return e.validateRepeated(vctx, fieldRules, rule.Repeated, fieldValue.List(), field, fieldPath, errors)
  223. case *validate.FieldRules_Map:
  224. return e.validateMap(vctx, fieldRules, rule.Map, fieldValue.Map(), field, fieldPath, errors)
  225. }
  226. return nil
  227. }
  228. // shouldIgnoreField 检查是否应该忽略字段验证
  229. func (e *Engine) shouldIgnoreField(
  230. rules *validate.FieldRules,
  231. hasValue bool,
  232. value protoreflect.Value,
  233. field protoreflect.FieldDescriptor,
  234. ) bool {
  235. switch rules.Ignore {
  236. case validate.IgnoreRule_IGNORE_ALWAYS:
  237. return true
  238. case validate.IgnoreRule_IGNORE_IF_ZERO:
  239. return !hasValue || isZeroValue(value, field)
  240. case validate.IgnoreRule_IGNORE_NEVER:
  241. return false
  242. default:
  243. // IGNORE_UNSPECIFIED: 未设置时不验证
  244. return !hasValue
  245. }
  246. }
  247. // isZeroValue 检查是否为零值
  248. func isZeroValue(value protoreflect.Value, field protoreflect.FieldDescriptor) bool {
  249. switch field.Kind() {
  250. case protoreflect.BoolKind:
  251. return !value.Bool()
  252. case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
  253. protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
  254. return value.Int() == 0
  255. case protoreflect.Uint32Kind, protoreflect.Fixed32Kind,
  256. protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
  257. return value.Uint() == 0
  258. case protoreflect.FloatKind, protoreflect.DoubleKind:
  259. return value.Float() == 0.0
  260. case protoreflect.StringKind:
  261. return value.String() == ""
  262. case protoreflect.BytesKind:
  263. return len(value.Bytes()) == 0
  264. case protoreflect.MessageKind:
  265. return !value.Message().IsValid()
  266. default:
  267. return false
  268. }
  269. }
  270. // validateOneofs 验证所有 oneof
  271. func (e *Engine) validateOneofs(
  272. vctx *ValidationContext,
  273. msgReflect protoreflect.Message,
  274. msgDescriptor protoreflect.MessageDescriptor,
  275. errors *ValidationErrors,
  276. ) error {
  277. oneofs := msgDescriptor.Oneofs()
  278. for i := 0; i < oneofs.Len(); i++ {
  279. oneof := oneofs.Get(i)
  280. // 获取 oneof 选项
  281. opts := oneof.Options()
  282. if opts == nil {
  283. continue
  284. }
  285. // 检查是否有验证规则
  286. if !proto.HasExtension(opts, validate.E_Oneof) {
  287. continue
  288. }
  289. // 获取 oneof 验证规则
  290. oneofRules := proto.GetExtension(opts, validate.E_Oneof).(*validate.OneofRules)
  291. if oneofRules == nil {
  292. continue
  293. }
  294. // 验证 required
  295. if oneofRules.Required {
  296. hasField := false
  297. fields := oneof.Fields()
  298. for j := 0; j < fields.Len(); j++ {
  299. if msgReflect.Has(fields.Get(j)) {
  300. hasField = true
  301. break
  302. }
  303. }
  304. if !hasField {
  305. errors.Add(NewValidationError(
  306. string(oneof.Name()),
  307. ErrCodeRequired,
  308. fmt.Sprintf("oneof %s 必须选择一个字段", oneof.Name()),
  309. ))
  310. if vctx.ShouldFailFast() {
  311. return errors
  312. }
  313. }
  314. }
  315. }
  316. return nil
  317. }
  318. // executeCELRule 执行 CEL 规则
  319. func (e *Engine) executeCELRule(
  320. vctx *ValidationContext,
  321. rule *validate.Rule,
  322. msg proto.Message,
  323. fieldPath string,
  324. errors *ValidationErrors,
  325. ) error {
  326. // 执行表达式
  327. result, err := e.celExecutor.EvaluateWithProto(rule.Expression, msg)
  328. if err != nil {
  329. // CEL 执行错误
  330. errors.Add(&ValidationError{
  331. FieldPath: fieldPath,
  332. RuleID: rule.Id,
  333. Message: fmt.Sprintf("CEL 表达式执行失败: %v", err),
  334. })
  335. return nil
  336. }
  337. // 如果验证失败
  338. if !result {
  339. message := rule.Message
  340. if message == "" && vctx.Language == "en-US" && rule.MessageEn != "" {
  341. message = rule.MessageEn
  342. }
  343. if message == "" {
  344. message = fmt.Sprintf("验证规则 %s 失败", rule.Id)
  345. }
  346. errors.Add(&ValidationError{
  347. FieldPath: fieldPath,
  348. RuleID: rule.Id,
  349. Message: message,
  350. MessageEN: rule.MessageEn,
  351. })
  352. }
  353. return nil
  354. }
  355. // executeCELFieldRule 执行字段级 CEL 规则
  356. func (e *Engine) executeCELFieldRule(
  357. vctx *ValidationContext,
  358. rule *validate.Rule,
  359. fieldValue interface{},
  360. parent proto.Message,
  361. fieldPath string,
  362. errors *ValidationErrors,
  363. ) error {
  364. // 执行表达式
  365. result, err := e.celExecutor.EvaluateFieldRule(rule.Expression, fieldValue, parent)
  366. if err != nil {
  367. // CEL 执行错误
  368. errors.Add(&ValidationError{
  369. FieldPath: fieldPath,
  370. RuleID: rule.Id,
  371. Message: fmt.Sprintf("CEL 表达式执行失败: %v", err),
  372. })
  373. return nil
  374. }
  375. // 如果验证失败
  376. if !result {
  377. message := rule.Message
  378. if message == "" && vctx.Language == "en-US" && rule.MessageEn != "" {
  379. message = rule.MessageEn
  380. }
  381. if message == "" {
  382. message = fmt.Sprintf("验证规则 %s 失败", rule.Id)
  383. }
  384. errors.Add(&ValidationError{
  385. FieldPath: fieldPath,
  386. RuleID: rule.Id,
  387. Message: message,
  388. MessageEN: rule.MessageEn,
  389. Value: fieldValue,
  390. })
  391. }
  392. return nil
  393. }
  394. // getErrorMessage 获取错误消息,按优先级返回
  395. // 优先级: 特定消息(required_message) > 通用消息(error_message) > 默认消息
  396. func (e *Engine) getErrorMessage(
  397. fieldRules *validate.FieldRules,
  398. vctx *ValidationContext,
  399. ruleType string,
  400. defaultMessage string,
  401. ) string {
  402. // 1. 检查是否有特定规则的自定义消息 (如 required_message)
  403. if ruleType == "required" {
  404. if vctx.Language == "en-US" && fieldRules.RequiredMessageEn != "" {
  405. return fieldRules.RequiredMessageEn
  406. }
  407. if fieldRules.RequiredMessage != "" {
  408. return fieldRules.RequiredMessage
  409. }
  410. }
  411. // 2. 检查通用自定义消息 (error_message)
  412. if vctx.Language == "en-US" && fieldRules.ErrorMessageEn != "" {
  413. return fieldRules.ErrorMessageEn
  414. }
  415. if fieldRules.ErrorMessage != "" {
  416. return fieldRules.ErrorMessage
  417. }
  418. // 3. 返回默认消息
  419. return defaultMessage
  420. }
  421. // ValidateField 验证单个字段(外部接口)
  422. func (e *Engine) ValidateField(vctx *ValidationContext, fieldPath string, value interface{}, rules interface{}) error {
  423. errors := &ValidationErrors{}
  424. // TODO: 实现单字段验证逻辑
  425. if errors.HasErrors() {
  426. return errors.ToKratosError()
  427. }
  428. return nil
  429. }