Răsfoiți Sursa

fix(core-module-mt): 修复 system-config-module 的 Redis 调用类型错误

- 在 SystemConfigServiceMt 中添加 formatTenantConfigKey 辅助函数
- 将 Redis 键格式改为 system_config:{tenantId}:{configKey} 实现租户隔离
- 在 RedisUtil 中添加 clearTenantSystemConfigs 方法
- 修复集成测试文件中的 redisUtil 直接调用

采用方案B:租户隔离在服务层处理,不影响其他模块

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 6 zile în urmă
părinte
comite
1776ebb3cb

+ 10 - 18
mini-ui-packages/yongren-order-management-ui/src/pages/OrderList/OrderList.tsx

@@ -326,11 +326,6 @@ const OrderList: React.FC = () => {
   })
 
 
-  const handleSearch = () => {
-    console.debug('搜索关键词:', searchKeyword)
-    // 使用refetch重新获取数据,queryKey变化会自动触发重新查询
-    refetch()
-  }
 
   const handleStatusFilter = (status: OrderStatus) => {
     setActiveStatus(status)
@@ -393,19 +388,16 @@ const OrderList: React.FC = () => {
         </View>
 
         {/* 搜索栏 */}
-        <View className="p-4 border-b border-gray-200">
-          <Input
-            className="border border-gray-300 rounded-lg px-3 py-2 text-sm w-full"
-            placeholder="按订单号、人才姓名搜索"
-            value={searchKeyword}
-            onInput={(e) => setSearchKeyword(e.detail.value)}
-          />
-          <View
-            className="flex justify-center items-center bg-gray-100 text-gray-800 text-xs px-3 py-2 rounded-lg mt-2 w-full"
-            onClick={handleSearch}
-          >
-            <Text className="i-heroicons-magnifying-glass-20-solid mr-1" />
-            <Text>搜索</Text>
+        <View className="px-4 py-3">
+          <View className="flex items-center bg-gray-100 rounded-lg px-4 py-2">
+            <Text className="i-heroicons-magnifying-glass-20-solid text-gray-400 mr-2" />
+            <Input
+              type="text"
+              placeholder="按订单号、人才姓名搜索"
+              className="w-full bg-transparent outline-none text-sm"
+              value={searchKeyword}
+              onInput={(e) => setSearchKeyword(e.detail.value)}
+            />
           </View>
         </View>
 

+ 23 - 13
packages/core-module-mt/system-config-module-mt/src/services/system-config.service.mt.ts

@@ -3,6 +3,13 @@ import { DataSource, In } from 'typeorm';
 import { SystemConfigMt } from '../entities/system-config.entity.mt';
 import { redisUtil } from '@d8d/shared-utils';
 
+/**
+ * 格式化租户系统配置的Redis键
+ */
+function formatTenantConfigKey(tenantId: number, configKey: string): string {
+  return `system_config:${tenantId}:${configKey}`;
+}
+
 export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
   constructor(dataSource: DataSource) {
     super(dataSource, SystemConfigMt, {
@@ -23,7 +30,8 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
    */
   async getConfigByKey(configKey: string, tenantId: number): Promise<string | null> {
     // 1. 先尝试从缓存获取
-    const cachedValue = await redisUtil.getSystemConfig(tenantId, configKey);
+    const cacheKey = formatTenantConfigKey(tenantId, configKey);
+    const cachedValue = await redisUtil.getSystemConfig(cacheKey);
 
     // 2. 如果缓存命中且不是空值缓存
     if (cachedValue !== null && !redisUtil.isNullValue(cachedValue)) {
@@ -44,10 +52,10 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
     // 5. 将结果写入缓存
     if (configValue !== null) {
       // 正常值,缓存1小时
-      await redisUtil.setSystemConfig(tenantId, configKey, configValue, 3600);
+      await redisUtil.setSystemConfig(cacheKey, configValue, 3600);
     } else {
       // 空值,缓存5分钟防止缓存穿透
-      await redisUtil.setNullSystemConfig(tenantId, configKey, 300);
+      await redisUtil.setNullSystemConfig(cacheKey, 300);
     }
 
     return configValue;
@@ -66,14 +74,15 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
    */
   async getConfigsByKeys(configKeys: string[], tenantId: number): Promise<Record<string, string>> {
     // 1. 先尝试从缓存批量获取
-    const cachedValues = await redisUtil.getSystemConfigs(tenantId, configKeys);
+    const cacheKeys = configKeys.map(key => formatTenantConfigKey(tenantId, key));
+    const cachedValues = await redisUtil.getSystemConfigs(cacheKeys);
 
     const result: Record<string, string> = {};
     const missingKeys: string[] = [];
 
     // 2. 处理缓存命中的键
-    configKeys.forEach(key => {
-      const cachedValue = cachedValues[key];
+    configKeys.forEach((key, index) => {
+      const cachedValue = cachedValues[cacheKeys[index]];
       if (cachedValue !== null && !redisUtil.isNullValue(cachedValue)) {
         result[key] = cachedValue;
       } else if (cachedValue === null) {
@@ -103,13 +112,14 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
       const cachePromises: Promise<void>[] = [];
       missingKeys.forEach(key => {
         const dbValue = dbValues[key];
+        const cacheKey = formatTenantConfigKey(tenantId, key);
         if (dbValue !== undefined) {
           // 数据库中有值,添加到结果并缓存
           result[key] = dbValue;
-          cachePromises.push(redisUtil.setSystemConfig(tenantId, key, dbValue, 3600));
+          cachePromises.push(redisUtil.setSystemConfig(cacheKey, dbValue, 3600));
         } else {
           // 数据库中无值,设置空值缓存
-          cachePromises.push(redisUtil.setNullSystemConfig(tenantId, key, 300));
+          cachePromises.push(redisUtil.setNullSystemConfig(cacheKey, 300));
         }
       });
 
@@ -138,7 +148,7 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
       });
 
       // 清除相关缓存
-      await redisUtil.deleteSystemConfig(tenantId, configKey);
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(tenantId, configKey));
 
       return updatedConfig!;
     } else {
@@ -151,7 +161,7 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
       } as SystemConfigMt);
 
       // 清除相关缓存(如果之前有空值缓存)
-      await redisUtil.deleteSystemConfig(tenantId, configKey);
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(tenantId, configKey));
 
       return newConfig!;
     }
@@ -178,7 +188,7 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
     await this.delete(config.id);
 
     // 清除相关缓存
-    await redisUtil.deleteSystemConfig(tenantId, configKey);
+    await redisUtil.deleteSystemConfig(formatTenantConfigKey(tenantId, configKey));
 
     return true;
   }
@@ -192,7 +202,7 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
     if (updatedConfig) {
       // 清除相关缓存
       const effectiveTenantId = updatedConfig.tenantId ?? 0;
-      await redisUtil.deleteSystemConfig(effectiveTenantId, updatedConfig.configKey);
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(effectiveTenantId, updatedConfig.configKey));
     }
 
     return updatedConfig;
@@ -210,7 +220,7 @@ export class SystemConfigServiceMt extends GenericCrudService<SystemConfigMt> {
     if (result && config) {
       // 清除相关缓存
       const effectiveTenantId = config.tenantId ?? 0;
-      await redisUtil.deleteSystemConfig(effectiveTenantId, config.configKey);
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(effectiveTenantId, config.configKey));
     }
 
     return result;

+ 42 - 35
packages/core-module-mt/system-config-module-mt/tests/integration/system-config-redis-cache.integration.test.ts

@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest';
+import { describe, it, expect, beforeEach } from 'vitest';
 import { testClient } from 'hono/testing';
 import {
   IntegrationTestDatabase,
@@ -14,20 +14,27 @@ import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
 import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
 import { redisUtil } from '@d8d/shared-utils';
 
+/**
+ * 格式化租户系统配置的Redis键(测试用辅助函数)
+ */
+function formatTenantConfigKey(tenantId: number, configKey: string): string {
+  return `system_config:${tenantId}:${configKey}`;
+}
+
 // 设置集成测试钩子
 setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
 
 describe('系统配置Redis缓存集成测试', () => {
-  let client: ReturnType<typeof testClient<typeof systemConfigRoutesMt>>;
+  let _client: ReturnType<typeof testClient<typeof systemConfigRoutesMt>>;
   let authService: AuthService;
   let userService: UserServiceMt;
   let systemConfigService: SystemConfigServiceMt;
-  let testToken: string;
+  let _testToken: string;
   let testUser: any;
 
   beforeEach(async () => {
     // 创建测试客户端
-    client = testClient(systemConfigRoutesMt);
+    _client = testClient(systemConfigRoutesMt);
 
     // 获取数据源
     const dataSource = await IntegrationTestDatabase.getDataSource();
@@ -46,7 +53,7 @@ describe('系统配置Redis缓存集成测试', () => {
     });
 
     // 生成测试用户的token
-    testToken = authService.generateToken(testUser);
+    _testToken = authService.generateToken(testUser);
 
     // 清除测试前的缓存
     await redisUtil.clearTenantSystemConfigs(testUser.tenantId);
@@ -55,7 +62,7 @@ describe('系统配置Redis缓存集成测试', () => {
   describe('缓存命中测试', () => {
     it('应该从缓存中获取配置值', async () => {
       // 先创建配置
-      const config = await systemConfigService.create({
+      const _config = await systemConfigService.create({
         configKey: 'app.cache.test',
         configValue: 'cached-value',
         description: '缓存测试配置',
@@ -67,7 +74,7 @@ describe('系统配置Redis缓存集成测试', () => {
       expect(firstResult).toBe('cached-value');
 
       // 验证缓存已写入
-      const cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.cache.test');
+      const cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.cache.test'));
       expect(cachedValue).toBe('cached-value');
 
       // 第二次查询 - 应该从缓存获取
@@ -98,9 +105,10 @@ describe('系统配置Redis缓存集成测试', () => {
       expect(firstResult['app.feature2.enabled']).toBe('false');
 
       // 验证缓存已写入
-      const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, ['app.feature1.enabled', 'app.feature2.enabled']);
-      expect(cachedValues['app.feature1.enabled']).toBe('true');
-      expect(cachedValues['app.feature2.enabled']).toBe('false');
+      const cacheKeys = ['app.feature1.enabled', 'app.feature2.enabled'].map(key => formatTenantConfigKey(testUser.tenantId, key));
+      const cachedValues = await redisUtil.getSystemConfigs(cacheKeys);
+      expect(cachedValues[cacheKeys[0]]).toBe('true');
+      expect(cachedValues[cacheKeys[1]]).toBe('false');
 
       // 第二次批量查询 - 应该从缓存获取
       const secondResult = await systemConfigService.getConfigsByKeys(
@@ -115,7 +123,7 @@ describe('系统配置Redis缓存集成测试', () => {
   describe('缓存失效测试', () => {
     it('应该在配置更新时清除缓存', async () => {
       // 创建配置
-      const config = await systemConfigService.create({
+      const _config = await systemConfigService.create({
         configKey: 'app.update.test',
         configValue: 'initial-value',
         tenantId: testUser.tenantId
@@ -125,14 +133,14 @@ describe('系统配置Redis缓存集成测试', () => {
       await systemConfigService.getConfigByKey('app.update.test', testUser.tenantId);
 
       // 验证缓存已写入
-      let cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.update.test');
+      let cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.update.test'));
       expect(cachedValue).toBe('initial-value');
 
       // 更新配置
       await systemConfigService.setConfig('app.update.test', 'updated-value', testUser.tenantId, '更新后的描述');
 
       // 验证缓存已清除
-      cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.update.test');
+      cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.update.test'));
       expect(cachedValue).toBeNull();
 
       // 再次查询应该从数据库获取新值
@@ -142,7 +150,7 @@ describe('系统配置Redis缓存集成测试', () => {
 
     it('应该在配置删除时清除缓存', async () => {
       // 创建配置
-      const config = await systemConfigService.create({
+      const _config = await systemConfigService.create({
         configKey: 'app.delete.test',
         configValue: 'to-be-deleted',
         tenantId: testUser.tenantId
@@ -152,14 +160,14 @@ describe('系统配置Redis缓存集成测试', () => {
       await systemConfigService.getConfigByKey('app.delete.test', testUser.tenantId);
 
       // 验证缓存已写入
-      let cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.delete.test');
+      let cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.delete.test'));
       expect(cachedValue).toBe('to-be-deleted');
 
       // 删除配置
       await systemConfigService.deleteConfig('app.delete.test', testUser.tenantId);
 
       // 验证缓存已清除
-      cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.delete.test');
+      cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.delete.test'));
       expect(cachedValue).toBeNull();
     });
   });
@@ -173,7 +181,7 @@ describe('系统配置Redis缓存集成测试', () => {
       expect(firstResult).toBeNull();
 
       // 验证空值缓存已设置
-      const cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, nonExistentKey);
+      const cachedValue = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, nonExistentKey));
       expect(redisUtil.isNullValue(cachedValue)).toBe(true);
 
       // 第二次查询应该从空值缓存返回null
@@ -202,22 +210,23 @@ describe('系统配置Redis缓存集成测试', () => {
       expect(result[nonExistentKey]).toBeUndefined();
 
       // 验证空值缓存已设置
-      const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, [existentKey, nonExistentKey]);
-      expect(cachedValues[existentKey]).toBe('existent-value');
-      expect(redisUtil.isNullValue(cachedValues[nonExistentKey])).toBe(true);
+      const cacheKeys = [existentKey, nonExistentKey].map(key => formatTenantConfigKey(testUser.tenantId, key));
+      const cachedValues = await redisUtil.getSystemConfigs(cacheKeys);
+      expect(cachedValues[cacheKeys[0]]).toBe('existent-value');
+      expect(redisUtil.isNullValue(cachedValues[cacheKeys[1]])).toBe(true);
     });
   });
 
   describe('多租户缓存隔离测试', () => {
-    let tenant1User: any;
-    let tenant2User: any;
+    let _tenant1User: any;
+    let _tenant2User: any;
 
     beforeEach(async () => {
       const dataSource = await IntegrationTestDatabase.getDataSource();
       if (!dataSource) throw new Error('Database not initialized');
 
       // 创建租户1的用户
-      tenant1User = await TestDataFactory.createTestUser(dataSource, {
+      _tenant1User = await TestDataFactory.createTestUser(dataSource, {
         username: 'tenant1_user_cache',
         password: 'TestPassword123!',
         email: 'tenant1_cache@example.com',
@@ -225,7 +234,7 @@ describe('系统配置Redis缓存集成测试', () => {
       });
 
       // 创建租户2的用户
-      tenant2User = await TestDataFactory.createTestUser(dataSource, {
+      _tenant2User = await TestDataFactory.createTestUser(dataSource, {
         username: 'tenant2_user_cache',
         password: 'TestPassword123!',
         email: 'tenant2_cache@example.com',
@@ -263,8 +272,8 @@ describe('系统配置Redis缓存集成测试', () => {
       expect(tenant2Result).toBe('tenant2-value');
 
       // 验证缓存隔离
-      const tenant1Cached = await redisUtil.getSystemConfig(1, sharedConfigKey);
-      const tenant2Cached = await redisUtil.getSystemConfig(2, sharedConfigKey);
+      const tenant1Cached = await redisUtil.getSystemConfig(formatTenantConfigKey(1, sharedConfigKey));
+      const tenant2Cached = await redisUtil.getSystemConfig(formatTenantConfigKey(2, sharedConfigKey));
       expect(tenant1Cached).toBe('tenant1-value');
       expect(tenant2Cached).toBe('tenant2-value');
     });
@@ -289,15 +298,13 @@ describe('系统配置Redis缓存集成测试', () => {
       await systemConfigService.warmUpCache(testUser.tenantId);
 
       // 验证缓存已预热
-      const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, [
-        'app.login.enabled',
-        'app.payment.enabled',
-        'app.notification.enabled'
-      ]);
-
-      expect(cachedValues['app.login.enabled']).toBe('true');
-      expect(cachedValues['app.payment.enabled']).toBe('false');
-      expect(redisUtil.isNullValue(cachedValues['app.notification.enabled'])).toBe(true);
+      const warmupKeys = ['app.login.enabled', 'app.payment.enabled', 'app.notification.enabled'];
+      const cacheKeys = warmupKeys.map(key => formatTenantConfigKey(testUser.tenantId, key));
+      const cachedValues = await redisUtil.getSystemConfigs(cacheKeys);
+
+      expect(cachedValues[cacheKeys[0]]).toBe('true');
+      expect(cachedValues[cacheKeys[1]]).toBe('false');
+      expect(redisUtil.isNullValue(cachedValues[cacheKeys[2]])).toBe(true);
     });
   });
 });

+ 22 - 15
packages/core-module-mt/system-config-module-mt/tests/integration/system-config.routes.integration.test.ts

@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest';
+import { describe, it, expect, beforeEach } from 'vitest';
 import { testClient } from 'hono/testing';
 import {
   IntegrationTestDatabase,
@@ -14,6 +14,13 @@ import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
 import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
 import { redisUtil } from '@d8d/shared-utils';
 
+/**
+ * 格式化租户系统配置的Redis键(测试用辅助函数)
+ */
+function formatTenantConfigKey(tenantId: number, configKey: string): string {
+  return `system_config:${tenantId}:${configKey}`;
+}
+
 // 设置集成测试钩子
 setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
 
@@ -321,8 +328,8 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
   describe('自定义路由缓存刷新验证', () => {
     beforeEach(async () => {
       // 清除测试缓存
-      await redisUtil.deleteSystemConfig(testUser.tenantId, 'app.login.enabled');
-      await redisUtil.deleteSystemConfig(testUser.tenantId, 'app.payment.enabled');
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.login.enabled'));
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(testUser.tenantId, 'app.payment.enabled'));
     });
 
     it('应该通过自定义创建路由刷新缓存', async () => {
@@ -333,7 +340,7 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       };
 
       // 验证缓存初始为空
-      const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, configData.configKey);
+      const initialCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, configData.configKey));
       expect(initialCache).toBeNull();
 
       // 通过自定义路由创建配置
@@ -351,7 +358,7 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       expect(result.configValue).toBe(configData.configValue);
 
       // 验证缓存已被刷新(为空,因为setConfig会删除缓存)
-      const afterCreateCache = await redisUtil.getSystemConfig(testUser.tenantId, configData.configKey);
+      const afterCreateCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, configData.configKey));
       expect(afterCreateCache).toBeNull();
     });
 
@@ -369,8 +376,8 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       } as any);
 
       // 设置缓存
-      await redisUtil.setSystemConfig(testUser.tenantId, config.configKey, 'cached-value', 3600);
-      const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey);
+      await redisUtil.setSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey), 'cached-value', 3600);
+      const initialCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey));
       expect(initialCache).toBe('cached-value');
 
       // 通过自定义路由更新配置
@@ -393,7 +400,7 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       expect(result.configValue).toBe(updateData.configValue);
 
       // 验证缓存已被刷新
-      const afterUpdateCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey);
+      const afterUpdateCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey));
       expect(afterUpdateCache).toBeNull();
     });
 
@@ -411,8 +418,8 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       } as any);
 
       // 设置缓存
-      await redisUtil.setSystemConfig(testUser.tenantId, config.configKey, 'cached-value', 3600);
-      const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey);
+      await redisUtil.setSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey), 'cached-value', 3600);
+      const initialCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey));
       expect(initialCache).toBe('cached-value');
 
       // 通过自定义路由删除配置
@@ -427,7 +434,7 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       expect(response.status).toBe(204);
 
       // 验证缓存已被刷新
-      const afterDeleteCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey);
+      const afterDeleteCache = await redisUtil.getSystemConfig(formatTenantConfigKey(testUser.tenantId, config.configKey));
       expect(afterDeleteCache).toBeNull();
     });
   });
@@ -463,8 +470,8 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       tenant2Token = authService.generateToken(tenant2User);
 
       // 清除测试缓存
-      await redisUtil.deleteSystemConfig(1, 'app.shared.config');
-      await redisUtil.deleteSystemConfig(2, 'app.shared.config');
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(1, 'app.shared.config'));
+      await redisUtil.deleteSystemConfig(formatTenantConfigKey(2, 'app.shared.config'));
     });
 
     it('应该确保多租户缓存隔离', async () => {
@@ -497,8 +504,8 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
       expect(response2.status).toBe(201);
 
       // 验证租户1的缓存操作不影响租户2
-      const tenant1Cache = await redisUtil.getSystemConfig(1, configData.configKey);
-      const tenant2Cache = await redisUtil.getSystemConfig(2, configData.configKey);
+      const tenant1Cache = await redisUtil.getSystemConfig(formatTenantConfigKey(1, configData.configKey));
+      const tenant2Cache = await redisUtil.getSystemConfig(formatTenantConfigKey(2, configData.configKey));
 
       // 两个租户的缓存都应该是空的,因为setConfig会删除缓存
       expect(tenant1Cache).toBeNull();

+ 23 - 0
packages/shared-utils/src/utils/redis.util.ts

@@ -129,6 +129,29 @@ class RedisUtil {
   formatSystemConfigKey(configKey: string): string {
     return `system_config:${configKey}`;
   }
+
+  /**
+   * 清除指定租户的所有系统配置缓存
+   * 使用 SCAN 命令遍历匹配的键并删除
+   */
+  async clearTenantSystemConfigs(tenantId: number): Promise<void> {
+    const client = await this.connect();
+    const pattern = `system_config:${tenantId}:*`;
+    const keys: string[] = [];
+
+    // 使用 SCAN 遍历所有匹配的键
+    for await (const key of client.scanIterator({
+      MATCH: pattern,
+      COUNT: 100
+    })) {
+      keys.push(key);
+    }
+
+    // 批量删除找到的键
+    if (keys.length > 0) {
+      await client.del(keys);
+    }
+  }
 }
 
 export const redisUtil = RedisUtil.getInstance();