一、 需求
为保障多工厂(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.