梁华东
Published on 2025-08-12 / 24 Visits
0
0

iMOM自动隔离工厂方案

一、 需求

 

为保障多工厂(site)场景下的数据隔离安全,行业包自己的功能需要实现统一的 siteId 工厂隔离机制

1. 业务数据表需按 siteId 字段进行工厂级别的隔离。

2. 实体类通过@Table(value = "SieMpmProjectDynamic",enableSite =true )

3. 查询时,系统应默认在构造 QueryWrapper 时自动追加 siteId = 当前上下文站点 的过滤条件,确保数据隔离。

4. 特殊场景(如运营后台跨工厂查询、超管权限)需提供跳过 siteId 自动过滤的能力。

5. 当前用户所属站点信息通过上下文HuaweiTenantContextHolder 进行管理,并支持线程隔离。

 

 

二、 技术方案

1. 实体继承 BaseSiteEntity 实现 siteId 显式建模

@Table(value = "SieMpmProjectDynamic",enableSite =true )
public class MpmProjectDynamic extends BaseEntity<MpmProjectDynamic> {
    /**
     * ID
     */
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
}

 

2. 当前站点上下文持有器 HuaweiTenantContextHolder

通过 ThreadLocal 实现用户当前操作所属工厂的隔离上下文,方便在任意业务逻辑中获取:

 huaweiTenantContextInfo.setSiteId(request.getHeader(DataFilterEnum.SITE_ID.getCode()));
    huaweiTenantContextInfo.setSysUser(sysUser);
    huaweiTenantContextInfo.setSysTenant(sysTenant);
    HuaweiTenantContextHolder.set(huaweiTenantContextInfo);

 

3. 实现 siteId 自动追加

自定义 QueryInterceptor 实现 siteId 自动追加

利用 查询拦截器机制,对所有继承SiteEntity 的实体查询,自动追加 siteId = 当前站点 条件:

    /**
     * 处理查询工厂
     * @param queryWrapper
     */
    private void handleQuerySite(QueryWrapper queryWrapper) {
        /**
         * 查询添加多租户多工厂字段结束
         */
        if (tableInfo.getEnableSite()) {
            if (!Boolean.TRUE.equals(queryWrapper.getIgnoreSite())) {
                Optional<HuaweiTenantContextInfo> dmeTenantContextInfo = Optional.ofNullable(HuaweiTenantContextHolder.get());
                String siteId = dmeTenantContextInfo.map(t -> t.getSiteId()).orElse(null);
                queryWrapper.where(TenantEntity::getSiteId).eq(siteId);
            }
        }
    }

 

4. 特殊查询可选择跳过 siteId 限制

某些超级管理员或跨工厂业务可设置跳过 siteId 条件:

QueryWrapper wrapper = QueryWrapper.create()
    .setIgnoreSite(true)
    .and("status", "active");
 

 

5. 插入自动填充siteId

    /**
     * 自动填充siteID
     *
     * @param entities
     */
    private void autoFillSite(List<?> entities) {
        if (CollectionUtil.isEmpty(entities))
            return;
        Optional<HuaweiTenantContextInfo> dmeTenantContextInfo = Optional.ofNullable(HuaweiTenantContextHolder.get());
        String siteId = dmeTenantContextInfo.map(t -> t.getSiteId()).orElse(null);
        if (StringUtils.isEmpty(siteId))
            return;
        List<EntityMetaObject> entityMetaObjectList = DmeDelegatorJsonUtil.entityToMetaObjectList(entities);
        entityMetaObjectList.forEach(entityMetaObject -> {
            if (entityMetaObject.hasField("siteId")) {
                if (ObjectUtils.isEmpty(entityMetaObject.getValue("siteId"))) {
                    entityMetaObject.setValue("siteId", siteId);
                }
            }
        });
    }

6.  

 


Comment