Skip to content

数据处理功能

数据权限过滤

几乎在任何业务系统中,都离不开权限的设计。权限设计通常包含两个维度:功能权限 + 数据权限。功能权限业界普遍采用 RBAC(Role-Based Access Control,基于角色的访问控制) 方案,而数据权限则用于控制用户能够访问哪些具体的数据。

数据权限功能是应用程序中的核心访问控制机制,确保只有经过授权的用户或角色才能访问特定数据。其设计意义不仅在于保障数据的安全性与完整性,还能显著提升应用程序的可用性和性能:

  • 性能优化:通过限制用户的数据访问范围,减少不必要的数据查询和操作,提高系统响应速度
  • 体验提升:根据用户角色和权限动态展示数据和功能,使用户只看到与其相关的内容,避免信息过载

HMX 平台采用 轻量级、灵活可控 的数据权限设计思路,不追求"万能"的数据权限解决方案,而是将核心能力交给框架,将复杂业务逻辑交给业务系统自身实现。

核心设计理念

  • 数据权限不是万能的,基础的由框架平台提供,复杂的由业务系统自己实现。

平台不提供类似"订单金额小于 100 万"这类强业务属性的自动数据过滤,这类需求应由业务系统根据自身场景自行设计与实现。

1. 静态元数据权限配置

平台将 元数据或键值对 配置为数据资源,并为角色授予这些静态元数据的访问权限:

数据资源示例:工厂数据资源

├── 工厂编码:1001(华东工厂)
├── 工厂编码:1002(华南工厂)
└── 工厂编码:1003(华北工厂)

角色授权示例:张总

└── 具备 1001 工厂的数据权限

2. 业务系统自主控制

业务系统获取当前角色所拥有的静态元数据权限(如工厂编码列表),然后根据实际需求 自主决定 是否使用 SQL 的 IN 表达式来过滤数据范围(使用 linq 查询,不是真的 sql in)

3. 框架能力边界

能力层级平台提供的功能业务系统负责
数据资源定义✅ 支持元数据/键值对配置-
角色权限授予✅ 支持角色与数据资源关联-
权限查询接口✅ 提供获取角色数据权限的 API-
SQL IN 条件拼接❌ 不自动拦截拼接✅ 业务系统自主实现
复杂业务逻辑过滤❌ 不提供(如:金额 < 100 万)✅ 业务系统自行设计

设计优势

  • 灵活可控:业务系统可以根据场景决定是否启用数据权限过滤,避免框架过度侵入
  • 简单易懂:不追求复杂的数据规则引擎,降低开发人员学习成本
  • 高性能:避免框架层面的自动 SQL 拦截带来的性能损耗
  • 边界清晰:框架提供基础能力,业务逻辑由业务系统掌控,职责分明

HMX 平台的数据权限设计遵循 "框架提供基础,业务掌控复杂" 的原则,通过静态元数据配置 + 角色授权 + 业务系统自主拼接 SQL IN 表达式的轻量级方案,既满足了常见的数据权限隔离需求,又避免了过度设计带来的复杂度和性能开销。对于复杂的业务规则过滤(如动态条件、多维度组合等),由业务系统根据实际场景自行实现,确保平台的通用性与灵活性。

连表查询增强

在钢铁制造业务系统中,多表关联查询是日常开发中无法回避的核心需求。例如查询工单时需要关联产线信息、查询质检记录时需要关联订单和物料信息、查询库存时需要关联仓库和库位信息等。LINQ to DB 作为 .NET 生态下的数据访问增强工具,将连表查询作为一等公民,通过 LINQ 表达式即可实现类型安全、智能感知、可读性强的多表关联查询,大幅简化开发效率。HMX 平台采用 LINQ to DB 作为数据访问增强工具,在保留原生 SQL 灵活性的同时,提供强类型 LINQ 查询能力。

核心特性

  • 原生连表支持:无需任何扩展即可使用 joinleft joininner join 等标准 SQL 关联语法
  • 类型安全:通过 Lambda 表达式访问字段,编译时检查正确性,避免运行时字段名错误
  • IDE 智能感知:表名、字段名自动补全,重构时自动更新引用
  • LINQ 标准语法:与操作内存集合的写法一致,降低学习成本

业务场景一:生产工单查询(订单 + 产线 + 排产计划)

生产排产时需要查询工单详情,同时显示关联的销售订单号、产线名称、排产计划量等信息。

csharp
/// <summary>
/// 分页查询生产工单列表(含订单信息、产线信息、排产计划)
/// </summary>
public async Task<PageResult<WorkOrderDto>> GetWorkOrderPage(WorkOrderQuery request)
{
    var query = from workOrder in _dbContext.PpWorkOrder
                join salesOrder in _dbContext.SalesOrder
                    on workOrder.OrderId equals salesOrder.Id
                join productionLine in _dbContext.BaseProductionLine
                    on workOrder.LineId equals productionLine.Id
                join schedule in _dbContext.PpScheduleDetail
                    on workOrder.Id equals schedule.WorkOrderId into scheduleGroup
                from schedule in scheduleGroup.DefaultIfEmpty()
                where workOrder.DelFlag == "0" && salesOrder.DelFlag == "0"
                select new WorkOrderDto
                {
                    WorkOrderId = workOrder.Id,
                    WorkOrderNo = workOrder.OrderNo,
                    SalesOrderNo = salesOrder.OrderNo,
                    CustomerName = salesOrder.CustomerName,
                    ProductName = salesOrder.ProductName,
                    ProductSpec = salesOrder.ProductSpec,
                    PlanQty = workOrder.PlanQty,
                    CompletedQty = workOrder.CompletedQty,
                    ProductionLineName = productionLine.LineName,
                    ScheduleQty = schedule != null ? schedule.PlanQty : 0,
                    PlanStartTime = workOrder.PlanStartTime,
                    PlanEndTime = workOrder.PlanEndTime,
                    WorkOrderStatus = workOrder.Status
                };

    return await query.ToPagedListAsync(request);
}

HiMind 工业互联网平台 技术文档