、后端(Spring Boot+MyBatis-Plus)、前端(Vue3+TypeScript))
数据库sql serverCREATE TABLE [dbo].[Activity]( [act_ID] [int] IDENTITY(1,1) NOT NULL, [act_ConcretWorkflowID] [int] NOT NULL, [act_UserDefinedNo] [varchar](20) NOT NULL, [act_ActorName] [varchar](40) NOT NULL, [act_Name] [varchar](100) NOT NULL, [act_IsStartNode] [bit] NOT NULL, [act_DefaultActor] [varchar](15) NOT NULL, [act_Remark] [varchar](200) NULL, [act_ExecuteType] [int] NOT NULL, [act_FilterType] [int] NOT NULL, [act_WithdrawType] [int] NULL, [act_ExecuteAction] [varchar](50) NULL, [act_WithdrawAction] [varchar](50) NULL, [act_Idea] [varchar](500) NULL, [act_FilterValue] [varchar](500) NULL, [act_Classified] [varchar](100) NULL, [act_File] [image] NULL, [act_FileName] [varchar](255) NULL, CONSTRAINT [PK_Activity] PRIMARY KEY CLUSTERED ( [act_ID] ASC )WITH (PAD_INDEX OFF, STATISTICS_NORECOMPUTE OFF, IGNORE_DUP_KEY OFF, ALLOW_ROW_LOCKS ON, ALLOW_PAGE_LOCKS ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]提交方式数据表字段名是 act_ExecuteType数据类型是 int后端Spring BootMyBatis-Plus枚举的定义src/main/java/com/weiyu/enumeration/ActivitySubmitMode.javapackage com.weiyu.enumeration; import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; /** * 活动提交方式枚举 */ Getter public enum ActivitySubmitMode { /** * 提交给指定人员 */ PERSONNEL(0, 提交给指定人员), /** * 提交给角色或人 */ ROLE(1, 提交给角色或人); EnumValue // MyBatis-Plus 数据库映射标记使用该字段的值存入数据库保存到数据库就是0、1、2等整数 JsonValue // Jackson JSON 序列化标记使用该字段的值进行序列化前端接收到的就是0、1、2等数字 private final Integer value; //JsonValue // Jackson JSON 序列化标记使用该字段的值进行序列化前端接收到的就是提交给指定人员、提交给角色或人等字符串 private final String displayName; ActivitySubmitMode(Integer value, String displayName) { this.value value; this.displayName displayName; } }实体类的定义src/main/java/com/weiyu/model/Activity.javapackage com.weiyu.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.weiyu.enumeration.ActivityRejectMode; import com.weiyu.enumeration.ActivitySubmitMode; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * 活动实体类 */ Data NoArgsConstructor EqualsAndHashCode(callSuper false) TableName(Activity) public class Activity { /** * 主键id */ TableId(value act_ID, type IdType.AUTO) private Integer id; /** * 流程版本id */ TableField(act_ConcretWorkflowID) NotNull Min(1) private Integer concretWorkflowId; /** * 活动名称 */ TableField(act_Name) NotBlank Size(max 100) private String name; /** * 是否首活动启动节点 */ TableField(act_IsStartNode) NotNull private Boolean isStartNode; /** * 办理意见 */ TableField(act_Idea) Size(max 500) private String idea; /** * 过滤模式 */ TableField(act_FilterType) NotNull Min(0) private Integer filterType; /** * 过滤内容 */ TableField(act_FilterValue) Size(max 500) private String filterValue; /** * 提交方式 */ TableField(act_ExecuteType) private ActivitySubmitMode submitMode; /** * 退回方式 */ TableField(act_WithdrawType) private ActivityRejectMode rejectMode; }VO的定义src/main/java/com/weiyu/model/AuditOptionVO.javapackage com.weiyu.model; import com.weiyu.enumeration.ActivityRejectMode; import com.weiyu.enumeration.ActivitySubmitMode; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * 办理选项 VO */ Data NoArgsConstructor public class AuditOptionVO { /** * 是否最后办理 */ private Boolean isLastAudit; /** * 办理意见选项 */ private ListString auditIdeaOptions; /** * 目标办理人角色成员选项 */ private ListRoleMemberDTO auditMemberOptions; /** * 目标活动名称 */ private String targetActivityName; /** * 提交方式 */ private ActivitySubmitMode submitMode; /** * 退回方式 */ private ActivityRejectMode rejectMode; }前端Vue3TypeScript枚举的定义src\types\Workflow.enum.ts/** * 活动提交方式枚举 */ export enum ActivitySubmitType { /** * 提交给指定人员 */ PERSONNEL 0, /** * 提交给角色或人 */ ROLE 1 }响应接口的定义src\types\Workflow.ts/** * 办理选项 */ export interface AuditOption { /** 是否最后办理 */ isLastAudit: boolean; /** 办理意见选项 */ auditIdeaOptions: string[]; /** 目标办理人角色成员选项 */ auditMemberOptions: RoleMember[]; /** 目标活动名称 */ targetActivityName: string; /** 提交方式 */ submitMode: ActivitySubmitType; /** 退回方式 */ rejectMode: ActivityRejectMode; }分析讲解在 Spring Boot 中使用 MyBatis-Plus 时实体类直接使用枚举类型是可行的但需要根据你期望的数据库存储值进行额外配置。你当前的代码存在一些潜在问题下面详细分析并给出建议。一、当前代码存在的问题1. 枚举字段的默认映射规则MyBatis-Plus基于 MyBatis默认使用EnumTypeHandler处理枚举类型它会存储枚举的name()字符串 或者ordinal()序号具体取决于全局配置默认可能是EnumOrdinalTypeHandler或EnumTypeHandler取决于版本和配置。不会自动使用枚举中自定义的value字段如你定义的0和1。如果你的数据库对应字段是整型比如submit_mode列类型为INT而 MyBatis-Plus 存储了枚举名称字符串就会导致类型不匹配异常如果存储了序号虽然类型匹配整数但序号可能与你期望的value不一致且枚举顺序变化会导致历史数据错乱。2. 缺少必要的注解你使用了TableField注解指定了数据库列名但没有告诉 MyBatis-Plus 如何将枚举转换为数据库值。因此 MyBatis-Plus 会使用默认处理器可能不是你想要的整数值。3. 枚举中的value字段未与 MyBatis-Plus 关联你的枚举类定义了value字段并提供了构造方法但没有通过任何注解标记它作为数据库映射的值所以 MyBatis-Plus 完全不知道这个字段的存在。二、如何正确实现存储枚举的整数值方案一使用EnumValue注解推荐MyBatis-Plus 提供了EnumValue注解可以标记在枚举的字段上指定该字段的值作为数据库存储值。修改你的枚举类javapackage com.weiyu.enumeration; import com.baomidou.mybatisplus.annotation.EnumValue; public enum ActivitySubmitMode { PERSONNEL(0), ROLE(1); EnumValue // 标记使用该字段的值存入数据库 private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } // 可以添加 getter 方法方便业务中使用 public Integer getValue() { return value; } }同样修改ActivityRejectMode枚举。这样配置后当 MyBatis-Plus 保存Activity对象时会将submitMode枚举的value字段值0 或 1写入数据库。查询时也会根据数据库的整数值自动转换为对应的枚举实例需要枚举中定义对应的值且值唯一。方案二实现IEnum接口MyBatis-Plus 还提供了IEnum接口枚举实现该接口并重写getValue()方法即可。javapublic enum ActivitySubmitMode implements IEnumInteger { PERSONNEL(0), ROLE(1); private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } Override public Integer getValue() { return value; } }效果与EnumValue类似但IEnum接口可能更适用于需要序列化等场景。方案三自定义类型处理器如果需求复杂例如需要额外的转换逻辑可以编写自定义的TypeHandler但通常前两种方案已经足够。三、实体类的其他注意事项1. 数据库列类型确保数据库表字段submit_mode和reject_mode的类型与存储值匹配如INT或TINYINT。如果使用EnumValue标记的字段是Integer数据库应为整数类型。2. MyBatis-Plus 版本EnumValue注解在 MyBatis-Plus 的较新版本中均支持建议使用最新稳定版。3. 布尔类型字段你的isStartNode字段是Boolean类型MyBatis-Plus 会自动处理通常数据库对应TINYINT(1)或BIT无需额外配置。4. 验证注解NotNull、Min等验证注解在实体类中定义是合理的会在 Controller 层触发校验需配合Valid使用。四、总结当前代码不可直接使用因为 MyBatis-Plus 默认无法将枚举转换为自定义的整数值。你需要在枚举的value字段上添加EnumValue注解或实现IEnum接口。确保数据库字段类型与value类型一致。确认 MyBatis-Plus 配置正确通常无需额外配置注解即生效。修改后的枚举示例javapublic enum ActivitySubmitMode { PERSONNEL(0), ROLE(1); EnumValue private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } }实体类保持不变即可。这样你的设计就是可行且优雅的既保持了代码的可读性使用枚举又实现了数据库存储整数值的灵活性。枚举中可以使用Getter吗在 Java 枚举中使用 Lombok 的Getter注解是完全可行的它能够为枚举的字段自动生成 getter 方法简化代码。一、基本用法在枚举上使用Getter与在普通类中类似。你可以在枚举类上添加Getter为所有字段生成 getter。在特定字段上添加Getter仅为该字段生成 getter。示例代码javaimport lombok.Getter; Getter public enum ActivitySubmitMode { PERSONNEL(0), ROLE(1); private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } }编译后Lombok 会自动生成getValue()方法你可以在业务代码中通过ActivitySubmitMode.PERSONNEL.getValue()获取枚举对应的整数值。二、注意事项1. 枚举字段的不可变性枚举的字段通常是final的推荐如此Lombok 生成的 getter 方法只是读取字段值不会尝试修改因此完全兼容。2. 构造器默认私有枚举的构造器默认为privateLombok 不会干涉这一点你仍然需要自己编写构造器或使用AllArgsConstructor简化但要注意枚举构造器必须是私有的Lombok 的AllArgsConstructor默认生成 public 构造器需手动调整因此更建议手动编写构造器。3. 使用Data需谨慎Data包含ToString、EqualsAndHashCode、Getter、Setter等。在枚举上使用Data可能会生成setter方法但枚举字段不可变setter 没有意义且toString、equals、hashCode可能覆盖了枚举默认的实现通常不需要。因此一般只使用Getter即可。4. 结合 MyBatis-Plus 的EnumValue如果你使用 MyBatis-Plus 的EnumValue标记数据库存储字段那么为该字段生成 getter 方法通过Getter可以方便地在业务层获取其值但并非强制要求。EnumValue本身不依赖 getter。示例javapublic enum ActivitySubmitMode { PERSONNEL(0), ROLE(1); EnumValue Getter private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } }三、为什么要在枚举中使用 Getter代码简洁避免手动编写样板 getter 方法。方便取值在需要获取枚举关联值如数据库存储的整型代码时直接通过 getter 访问提高可读性。与框架集成某些框架可能通过 getter 访问属性如 JSON 序列化Getter可以确保符合 JavaBean 规范。四、总结可以而且推荐在枚举中使用Getter它能帮助保持代码简洁同时不会影响枚举自身的特性。只需注意在枚举上避免使用可能带来副作用的 Lombok 注解如Data、Setter并手动处理构造器即可。为什么前端vue3接收到的是submitMode: ROLE这是因为 Spring Boot 默认使用Jackson进行 JSON 序列化而 Jackson 在处理枚举类型时默认将枚举序列化为其名称name()即字符串ROLE。这与 MyBatis-Plus 的数据库映射你使用了EnumValue存储整数值是两套独立的机制互不影响。为什么会出现这种现象MyBatis-Plus 的EnumValue只控制实体类与数据库之间的转换写入/读取。当从数据库查询数据时MyBatis-Plus 会根据整数值自动组装成对应的枚举实例例如数据库存1会映射为ActivitySubmitMode.ROLE。Jackson 序列化当 Controller 返回Activity对象时Spring 会使用 Jackson 将对象转换为 JSON。Jackson 默认将枚举输出为Enum.name()因此你看到submitMode: ROLE。如何让前端收到你期望的值场景一前端需要整数值如 0 或 1你希望前端看到submitMode: 1可以使用 Jackson 的JsonValue注解指定序列化时使用枚举的哪个字段。修改枚举类javaimport com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; Getter public enum ActivitySubmitMode { PERSONNEL(0), ROLE(1); EnumValue // MyBatis-Plus 数据库映射 JsonValue // Jackson JSON 序列化 private final Integer value; ActivitySubmitMode(Integer value) { this.value value; } }加上JsonValue后Jackson 会使用value字段的值进行序列化前端收到的 JSON 将是submitMode: 1整数。场景二前端需要枚举名称当前现象如果你确实需要前端收到ROLE那么当前行为就是正确的无需修改。场景三前端需要自定义字符串如 角色如果希望返回更友好的显示文本可以在枚举中添加额外字段并用JsonValue标记该字段。javaGetter public enum ActivitySubmitMode { PERSONNEL(0, 指定人员), ROLE(1, 角色或人); EnumValue private final Integer value; JsonValue private final String displayName; ActivitySubmitMode(Integer value, String displayName) { this.value value; this.displayName displayName; } }此时前端收到submitMode: 角色或人。注意事项JsonValue会影响整个枚举的序列化包括在 Map 中作为 key 等情况确保你了解其作用范围。如果同一个枚举需要不同的序列化方式例如在某些接口输出名称在某些接口输出代码建议使用DTO数据传输对象在 DTO 中定义需要的字段类型并在业务层手动转换。如果全局都需要将枚举输出为代码也可以配置 Jackson 的SerializationFeature.WRITE_ENUMS_USING_TO_STRING或自定义序列化器但JsonValue是最直接的方式。总结当前看到ROLE是 Jackson 的默认行为。若希望前端收到整数值只需在枚举的value字段上添加JsonValue注解。EnumValue和JsonValue可以共存分别服务于数据库映射和 JSON 序列化。这样你的实体类就能同时满足数据库存储整数、前端接收整数的需求了。