应将多步骤问卷校验集中到父级卡片统一执行,通过反射扫描顶层实体类(如surveyformdata)的bean validation注解批量验证;需确保实体类结构规范、字段可访问、类型明确,并在提交/保存草稿时触发全量校验,收集所有错误一次性反馈。

在多步骤问卷调查中,把所有字段校验逻辑集中到父级卡片(如主表单容器)统一执行,而不是每步各自校验,能避免重复、提升一致性。反射在这里的作用,是绕过“步骤隔离”,直接扫描所有子步骤组件绑定的字段实体类,读取其校验注解(如 @NotBlank、@Email、@Min 等),再批量验证——前提是这些字段已通过某种方式汇聚到一个顶层数据对象中。
确保字段实体具备可反射校验的基础结构
反射校验的前提是:所有步骤填写的数据最终都映射到一个统一的 Java 实体类(比如 SurveyFormData),且该类字段上标注了标准 Bean Validation 注解:
- 实体类必须是 public,含无参构造器(Lombok 的
@NoArgsConstructor可用); - 字段需为 public / protected,或使用
field.setAccessible(true)支持 private 字段访问; - 避免使用泛型擦除后无法获取类型信息的字段(如
List>),推荐明确类型如List<string></string>; - 若自定义注解(如
@RequiredIfStepCompleted),需确保@Retention(RetentionPolicy.RUNTIME)。
在持久化节点前触发一次全量反射校验
所谓“持久化节点”,通常指用户点击“提交”或“保存草稿”时的最后拦截点。此时应:
- 从 Zustand / Redux / 或本地状态中取出完整表单数据对象(非分步快照,而是合并后的最终态);
- 调用反射校验工具方法,传入该对象及其 Class 类型;
- 遍历所有声明字段:
clazz.getDeclaredFields(); - 对每个字段,检查是否含校验注解(
field.isAnnotationPresent(NotBlank.class)),并用field.get(obj)取值执行对应逻辑(如判空、正则匹配、范围检查); - 收集全部错误信息,不提前中断,保证一次性反馈所有问题。
处理跨步骤字段依赖与条件校验
纯反射无法自动理解业务逻辑(例如“当步骤2选‘是’,步骤3的手机号必填”),但可通过注解扩展支持:
- 定义运行时注解
@DependsOn(field = "step2Answer", value = "YES"),在校验循环中识别并动态读取依赖字段值; - 将步骤标识(如
step: "step3")作为字段元数据写入注解(@Column(step = "step3")),便于按步骤分组错误提示; - 对重复节(repeat group)字段,反射时需结合索引处理,例如
answers[0].email、answers[1].email,需解析表达式或约定命名规则。
避免反射校验的常见陷阱
反射本身不处理数据转换和上下文,容易踩坑:
- 字段值为 null 时,
@NotNull校验会失败,但@NotBlank对 null 会抛 NPE —— 需先判空再调用String.trim().length(); - 时间/日期字段若为
LocalDateTime,而前端传的是字符串,反射取值前需确认已完成反序列化,否则取到的是原始字符串,注解校验失效; - 不要在反射校验中修改字段值(如自动 trim),这会污染原始数据;校验只负责报告,修正由 UI 层或格式化中间件完成;
- Zustand 持久化后恢复的数据,若未走完整 setter 流程,可能丢失部分计算属性 —— 校验前建议先调用
recomputeDerivedFields()类方法补全。










