feat(tenant): 租户-项目两级架构 Phase 2 — IoT + Ops 业务迁移
DO 迁移 (15个 TenantBaseDO → ProjectBaseDO): - IoT: IotDeviceDO - Ops 核心: OpsOrderDO, OpsOrderEventDO, OpsOrderDispatchDO, OpsOrderQueueDO, OpsBusAreaDO, OpsAreaDeviceRelationDO, OpsDeviceTrajectoryDO - Ops 保洁: OpsOrderCleanExtDO, OpsCleanerStatusDO, OpsCleanerPerformanceMonthlyDO, OpsInspectionRecordDO, OpsInspectionRecordItemDO - Ops 安保: OpsOrderSecurityExtDO, OpsAreaSecurityUserDO IoT 适配: - IotDeviceRespDTO 新增 projectId 字段 - IotDeviceMessage 新增 projectId 字段 - IotDeviceMessageServiceImpl.appendDeviceMessage() 设置 projectId - IotCleanRuleMessageHandler 嵌套 ProjectUtils.execute() 设置项目上下文 缓存改造: - ProjectRedisCacheManager extends TenantRedisCacheManager,追加 :projectId 后缀 - ViewshTenantAutoConfiguration 替换为 ProjectRedisCacheManager SQL 迁移脚本 (sql/mysql/project/): - 01-create-tables.sql: system_project + system_user_project 建表 - 02-default-data.sql: 默认项目 + 用户关联回填 - 03-alter-business-tables.sql: 15 张表添加 project_id (NULL → 回填 → NOT NULL → 索引) - 04-index-audit.sql: 现有索引审计 + project_id 补充建议 - 99-rollback.sql: 完整回滚方案 附带修复: - fix(ops): UserDispatchStatusServiceImpl 添加缺失的 KEY_PREFIX 常量 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import com.viewsh.framework.tenant.core.job.TenantJobAspect;
|
||||
import com.viewsh.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer;
|
||||
import com.viewsh.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor;
|
||||
import com.viewsh.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer;
|
||||
import com.viewsh.framework.tenant.core.redis.ProjectRedisCacheManager;
|
||||
import com.viewsh.framework.tenant.core.redis.TenantRedisCacheManager;
|
||||
import com.viewsh.framework.tenant.core.security.TenantSecurityWebFilter;
|
||||
import com.viewsh.framework.tenant.core.service.ProjectFrameworkService;
|
||||
@@ -266,8 +267,9 @@ public class ViewshTenantAutoConfiguration {
|
||||
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
|
||||
BatchStrategies.scan(viewshCacheProperties.getRedisScanBatchSize()));
|
||||
// 创建 TenantRedisCacheManager 对象
|
||||
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());
|
||||
// 创建 ProjectRedisCacheManager 对象(在租户隔离基础上叠加项目隔离)
|
||||
return new ProjectRedisCacheManager(cacheWriter, redisCacheConfiguration,
|
||||
tenantProperties.getIgnoreCaches(), tenantProperties.getIgnoreProjectCaches());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.viewsh.framework.tenant.core.redis;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.viewsh.framework.tenant.core.context.ProjectContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 多项目的 {@link org.springframework.data.redis.cache.RedisCacheManager} 实现类
|
||||
*
|
||||
* 在租户隔离的基础上,追加项目隔离后缀,格式为 name + ":" + tenantId + ":" + projectId
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
public class ProjectRedisCacheManager extends TenantRedisCacheManager {
|
||||
|
||||
private static final String SPLIT = "#";
|
||||
|
||||
private final Set<String> ignoreProjectCaches;
|
||||
|
||||
public ProjectRedisCacheManager(RedisCacheWriter cacheWriter,
|
||||
RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Set<String> ignoreTenantCaches,
|
||||
Set<String> ignoreProjectCaches) {
|
||||
super(cacheWriter, defaultCacheConfiguration, ignoreTenantCaches);
|
||||
this.ignoreProjectCaches = ignoreProjectCaches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
// 获取原始 cache name(去掉 # 后缀部分,# 后面是超时配置)
|
||||
String[] names = StrUtil.splitToArray(name, SPLIT);
|
||||
// 如果开启项目隔离,则在 name 上追加 projectId 后缀
|
||||
// 父类 TenantRedisCacheManager.getCache 会继续追加 tenantId,
|
||||
// 最终 key 格式:name[:projectId]:tenantId
|
||||
if (!ProjectContextHolder.isIgnore()
|
||||
&& ProjectContextHolder.getProjectId() != null
|
||||
&& !CollUtil.contains(ignoreProjectCaches, names[0])) {
|
||||
name = name + ":" + ProjectContextHolder.getProjectId();
|
||||
}
|
||||
// 继续基于父方法(父类追加租户后缀)
|
||||
return super.getCache(name);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user