数据库(sql server)

CREATE 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 Boot+MyBatis-Plus)

枚举的定义

 src/main/java/com/weiyu/enumeration/ActivitySubmitMode.java

package 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.java

package 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.java

package 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 List<String> auditIdeaOptions;
    /**
     * 目标办理人(角色成员)选项
     */
    private List<RoleMemberDTO> auditMemberOptions;
    /**
     * 目标活动名称
     */
    private String targetActivityName;
    /**
     * 提交方式
     */
    private ActivitySubmitMode submitMode;
    /**
     * 退回方式
     */
    private ActivityRejectMode rejectMode;
}

前端(Vue3+TypeScript)

枚举的定义

 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 注解,可以标记在枚举的字段上,指定该字段的值作为数据库存储值。

修改你的枚举类:

java

package 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() 方法即可。

java

public enum ActivitySubmitMode implements IEnum<Integer> {
    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 默认无法将枚举转换为自定义的整数值。你需要:

  1. 在枚举的 value 字段上添加 @EnumValue 注解(或实现 IEnum 接口)。

  2. 确保数据库字段类型与 value 类型一致。

  3. 确认 MyBatis-Plus 配置正确(通常无需额外配置,注解即生效)。

修改后的枚举示例:

java

public 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。

示例代码

java

import 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. 构造器默认私有

枚举的构造器默认为 private,Lombok 不会干涉这一点,你仍然需要自己编写构造器(或使用 @AllArgsConstructor 简化,但要注意枚举构造器必须是私有的,Lombok 的 @AllArgsConstructor 默认生成 public 构造器,需手动调整,因此更建议手动编写构造器)。

3. 使用 @Data 需谨慎

@Data 包含 @ToString@EqualsAndHashCode@Getter@Setter 等。在枚举上使用 @Data 可能会生成 setter 方法(但枚举字段不可变,setter 没有意义),且 toStringequalshashCode 可能覆盖了枚举默认的实现(通常不需要)。因此,一般只使用 @Getter 即可。

4. 结合 MyBatis-Plus 的 @EnumValue

如果你使用 MyBatis-Plus 的 @EnumValue 标记数据库存储字段,那么为该字段生成 getter 方法(通过 @Getter)可以方便地在业务层获取其值,但并非强制要求。@EnumValue 本身不依赖 getter。

示例:

java

public 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 注解,指定序列化时使用枚举的哪个字段。

修改枚举类:

java

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 数据库映射
    @JsonValue      // Jackson JSON 序列化
    private final Integer value;

    ActivitySubmitMode(Integer value) {
        this.value = value;
    }
}

加上 @JsonValue 后,Jackson 会使用 value 字段的值进行序列化,前端收到的 JSON 将是 submitMode: 1(整数)。

场景二:前端需要枚举名称(当前现象)

如果你确实需要前端收到 "ROLE",那么当前行为就是正确的,无需修改。

场景三:前端需要自定义字符串(如 "角色")

如果希望返回更友好的显示文本,可以在枚举中添加额外字段,并用 @JsonValue 标记该字段。

java

@Getter
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: "角色或人"


注意事项

  1. @JsonValue 会影响整个枚举的序列化,包括在 Map 中作为 key 等情况,确保你了解其作用范围。

  2. 如果同一个枚举需要不同的序列化方式(例如在某些接口输出名称,在某些接口输出代码),建议使用 DTO(数据传输对象),在 DTO 中定义需要的字段类型,并在业务层手动转换。

  3. 如果全局都需要将枚举输出为代码,也可以配置 Jackson 的 SerializationFeature.WRITE_ENUMS_USING_TO_STRING 或自定义序列化器,但 @JsonValue 是最直接的方式。


总结

  • 当前看到 "ROLE" 是 Jackson 的默认行为。

  • 若希望前端收到整数值,只需在枚举的 value 字段上添加 @JsonValue 注解。

  • @EnumValue 和 @JsonValue 可以共存,分别服务于数据库映射和 JSON 序列化。

这样,你的实体类就能同时满足数据库存储整数、前端接收整数的需求了。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐