
前言个人项目实践总结分享位掩码状态设计的完整方案。位掩码Bitmask状态设计方案使用 2 的 n 次幂来设计类型、状态字段通过位运算判断当前状态一、什么是位掩码位掩码是一种利用二进制位来表示多种状态或权限的技术。每个状态使用一个独立的二进制位1、2、4、8、16…通过位运算进行组合和判断。核心原理十进制二进制含义10001待审核20010已通过40100已发布81000已删除java// 一条数据 status 3二进制 0011 // 表示同时拥有待审核 已通过 // 判断是否已通过(3 2) 2 → true二、优缺点分析✅ 优点优点说明存储高效一个字段存储多个状态节省数据库空间查询高效位运算比字符串匹配快得多扩展方便新增类型只需增加新位不改变表结构组合灵活状态可自由组合无限搭配❌ 缺点缺点说明可读性差看到数字3不知道什么意思需要查文档查询不便WHERE (status 2) 2无法使用索引维护困难新人理解成本高学习曲线陡峭调试不便日志中看到数字需要手动换算扩展有限数据库 int 最多 32 位bigint 最多 64 位⚠️ 索引问题sql-- ❌ 位运算查询无法使用索引 SELECT * FROM task WHERE (status 2) 2; -- ✅ 等值查询可以使用索引 SELECT * FROM task WHERE status 3;三、适用场景场景推荐度原因权限控制⭐⭐⭐⭐⭐多角色组合查询少状态多配置开关⭐⭐⭐⭐⭐功能开关组合如用户设置业务状态流转⭐⭐状态通常互斥不需要组合频繁按状态查询⭐位运算导致索引失效四、完整实现方案1. 数据库层方案 A字段加注释状态少sqlCREATE TABLE task ( id INT PRIMARY KEY, status INT DEFAULT 1 COMMENT 1:待审核, 2:已通过, 4:已发布, 8:已删除 );方案 B字典表状态多/动态sql-- 字典表 CREATE TABLE sys_dict ( id INT PRIMARY KEY, dict_type VARCHAR(50) COMMENT 字典类型, dict_code INT COMMENT 字典值, dict_label VARCHAR(100) COMMENT 字典标签, sort INT COMMENT 排序 ); -- 示例数据 INSERT INTO sys_dict VALUES (1, task_status, 1, 待审核, 1); INSERT INTO sys_dict VALUES (2, task_status, 2, 已通过, 2); INSERT INTO sys_dict VALUES (3, task_status, 4, 已发布, 3); INSERT INTO sys_dict VALUES (4, task_status, 8, 已删除, 4);2. Java 枚举实现javapublic enum TaskStatus { PENDING(1, 待审核), APPROVED(2, 已通过), PUBLISHED(4, 已发布), DELETED(8, 已删除); private final int code; private final String label; TaskStatus(int code, String label) { this.code code; this.label label; } public int getCode() { return code; } public String getLabel() { return label; } /** * 根据 code 获取枚举 */ public static TaskStatus fromCode(int code) { for (TaskStatus status : values()) { if (status.code code) { return status; } } return null; } /** * 判断当前状态是否包含指定状态位运算 */ public boolean hasStatus(int flags) { return (flags code) code; } /** * 获取所有状态标签 */ public static ListString getLabels(int flags) { ListString labels new ArrayList(); for (TaskStatus status : values()) { if (status.hasStatus(flags)) { labels.add(status.getLabel()); } } return labels; } }3. 实体类使用javaEntity public class Task { private int status; // 数据库存储数字 /** * 判断是否已通过 */ public boolean isApproved() { return TaskStatus.APPROVED.hasStatus(status); } /** * 添加状态 */ public void addStatus(TaskStatus status) { this.status | status.getCode(); } /** * 移除状态 */ public void removeStatus(TaskStatus status) { this.status ~status.getCode(); } /** * 获取所有状态标签 */ public ListString getStatusLabels() { return TaskStatus.getLabels(status); } }4. 后端 API 返回javapublic class TaskDTO { private Integer status; // 数字 private String statusLabel; // 显示用文字 private ListString statusLabels; // 多状态组合 public static TaskDTO fromEntity(Task task) { TaskDTO dto new TaskDTO(); dto.setStatus(task.getStatus()); dto.setStatusLabel(TaskStatus.fromCode(task.getStatus()).getLabel()); dto.setStatusLabels(TaskStatus.getLabels(task.getStatus())); return dto; } }5. 前端显示TypeScript / JavaScripttypescript// 方式1前端硬编码 const statusMap { 1: 待审核, 2: 已通过, 4: 已发布, 8: 已删除 }; // 使用 span{{ statusMap[task.status] }}/span方式2后端接口获取typescript// 前端调用接口获取字典 const statusMap await getDict(task_status); const label statusMap[task.status];java// 后端提供字典接口 GetMapping(/dict/task-status) public ResultDtoMapInteger, String getTaskStatusDict() { MapInteger, String dict Arrays.stream(TaskStatus.values()) .collect(Collectors.toMap( TaskStatus::getCode, TaskStatus::getLabel )); return ResultDto.success(dict); }五、各层职责层级存储说明数据库数字存储高效减少空间字段备注文字方便 DBA 维护Java 枚举数字 → 文字代码层类型安全前端文字显示给用户text┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 前端 │ │ 后端 │ │ 数据库 │ │ │ │ │ │ │ │ 显示: 已通过 │ ←── │ 返回: 2 │ ←── │ 存: 2 │ │ │ │ 映射: 2已通过│ │ │ │ 不存数字 │ │ 使用枚举 │ │ 只存数字 │ └─────────────┘ └─────────────┘ └─────────────┘前端只显示文字永远不显示数字。六、替代方案方案优点缺点适用场景位掩码存储小、性能高可读性差、索引不友好权限、配置关联表索引友好、可扩展查询需 JOIN多对多关系JSON 字段灵活、可读性好查询复杂少量灵活字段多个布尔字段可读性最好、索引友好字段过多互斥状态推荐选择权限/配置开关→ 位掩码 ✅业务状态→ 枚举或关联表灵活扩展→ JSON 字段七、最佳实践✅ DO状态固定时使用枚举保证代码类型安全数据库字段加注释说明每个数字的含义前端永远显示文字不暴露数字提供字典接口支持动态维护数字只作为存储方式业务层都用枚举❌ DON’T不要在前端直接显示数字不要硬编码数字在代码中用枚举替代不要在频繁查询的字段上使用位运算索引失效不要超过数据库位长度限制int 32 位bigint 64 位八、总结位掩码设计是经典的计算机科学实践在权限控制、配置开关等场景下非常高效。但在业务状态流转中建议优先考虑可读性和可维护性。设计原则数据层存数字 → 业务层用枚举 → 展示层显文字让每一层做自己最擅长的事。✅如果你觉得这篇文章有帮助欢迎点赞、收藏、分享