2. 配方数据模型设计:实体定义、字段权限标记与数据归属

数据模型这东西,说白了就是给配方“画个像”。你想想看,一个配方在系统里到底长什么样?哪些字段能改、哪些不能改?它属于谁?这些问题不搞清楚,后面的权限控制根本无从下手。我个人习惯,在设计阶段就把这些边界划清楚,省得后期返工。

2.1 配方实体定义:核心字段与结构

我们先从最基础的实体定义开始。一个典型的配方实体,我通常会把它拆成三个层次:配方头、配方项和工艺参数。为什么这么分?因为不同层级的字段,权限敏感度完全不同。

层级 实体名称 核心字段示例 权限敏感度
L1 配方头 (RecipeHeader) 配方ID、名称、版本、状态、创建人、所属产线 高(元数据)
L2 配方项 (RecipeItem) 物料编码、用量、单位、替代料清单 中(BOM数据)
L3 工艺参数 (ProcessParam) 温度设定、压力值、时间窗口、公差范围 极高(核心工艺)

关键点:配方头中的“状态”字段(如:草稿、已审核、已发布、已锁定)是权限控制的开关。我在项目中遇到过,有人直接修改了已发布配方的状态,导致产线用了错误版本。所以,状态变更必须走审批流。

下面是一个简化后的配方实体定义代码示例,我用的是 Java 风格,但思路通用:

public class RecipeHeader {
    private String recipeId;          // 配方唯一标识
    private String recipeName;        // 配方名称
    private int version;              // 版本号
    private String status;            // 状态:DRAFT, REVIEWED, RELEASED, LOCKED
    private String ownerId;           // 所有者ID
    private String lineId;            // 所属产线ID
    private List<RecipeItem> items;   // 配方项列表
    private List<ProcessParam> params; // 工艺参数列表
    // getters/setters 省略
}

2.2 字段权限标记:谁可以改什么?

实体定义好了,接下来就是给每个字段打上“权限标签”。我习惯用位掩码或者枚举组合来做这件事。你想想看,一个字段可能有多种操作:可读、可写、可审批、可删除。用位掩码可以很优雅地组合这些权限。

举个例子,对于工艺参数中的“温度设定”字段:

  • 操作员:只能读,不能写(位掩码:0x01)
  • 工艺工程师:可读可写,但不能删除(位掩码:0x03)
  • 管理员:全部权限(位掩码:0x0F)

我的小技巧:在数据库里,我会额外加一个字段叫 permMask,用整数存储。比如 permMask = 3 表示可读+可写。这样查询时直接用位运算,性能很好。我曾经在一个高并发项目里用这种方式,权限判断耗时从毫秒级降到了微秒级。

下面是一个字段权限标记的配置示例:

@Entity
public class FieldPermission {
    private String fieldName;      // 字段名,如 "temperatureSet"
    private int readMask;          // 可读的角色位掩码
    private int writeMask;         // 可写的角色位掩码
    private int approveMask;       // 可审批的角色位掩码
    // 判断当前角色是否有写权限
    public boolean canWrite(int roleMask) {
        return (roleMask & writeMask) != 0;
    }
}

2.3 数据归属关系:配方到底属于谁?

这个问题看似简单,其实坑很多。配方不是孤立存在的,它和多个实体有归属关系。我一般会画一张归属关系图来理清脉络。

核心的归属关系包括:

  1. 用户 → 配方:配方有创建者和所有者。所有者可以转让。
  2. 角色 → 配方:某些配方只能由特定角色(如高级工程师)修改。
  3. 组织 → 配方:配方属于某个部门或工厂,跨组织访问需要授权。
  4. 产线 → 配方:配方绑定到具体产线,换线时配方自动切换。

避坑指南:我曾经犯过一个错误——只考虑了用户到配方的直接归属,忽略了组织归属。结果一个工厂的配方被另一个工厂的人误改了,因为两个工厂共用了同一个配方ID池。后来我加上了 orgId 作为分区键,才彻底解决。

下面我用一张 SVG 图来展示完整的配方数据归属关系:

配方数据归属关系图 配方数据归属关系图 配方 (Recipe) 用户 (User) 角色 (Role) 组织 (Org) 产线 (Line) 创建/所有 授权 归属 绑定 归属关系说明 1. 用户:配方的创建者和当前所有者,可转让 2. 角色:控制配方修改的操作权限 3. 组织:配方所属的部门/工厂,跨组织需授权 4. 产线:配方绑定的物理产线,换线自动切换

2.4 实战中的设计决策

最后,我想分享几个我在实际项目中踩过的坑,以及对应的设计决策:

  • 字段权限粒度:别一开始就搞到字段级,太细了维护成本高。我建议先做到实体级,等业务稳定了再细化到字段级。
  • 归属关系缓存:每次修改都查数据库确认归属?太慢了。我会在 Redis 里缓存一份 recipeId → {ownerId, orgId, lineId} 的映射,过期时间设 5 分钟。
  • 版本与归属联动:配方升级时,归属关系要不要变?我的经验是:版本升级不改变归属,除非显式转让。

总结一下:配方数据模型设计,核心就是三件事——实体定义(配方长什么样)、字段权限标记(谁可以改什么)、数据归属关系(配方属于谁)。这三件事搞定了,后面的权限控制逻辑就是水到渠成的事。