This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 809c40916 fix(consumer): show the subscription filter modes the API 
actually returns (#5260)
809c40916 is described below

commit 809c409163578fa138d770cb71dc8a11336053a8
Author: Apulupie <[email protected]>
AuthorDate: Fri Oct 9 19:30:04 2026 +0800

    fix(consumer): show the subscription filter modes the API actually returns 
(#5260)
    
    The consumer group table matched the subscription-mode column against the 
pre-#3349 Chinese display
    strings (`全量` / `Tag 过滤` / `SQL92 过滤`), while the API returns `TAG` / `SQL` 
/ `ALL`, so the
    column fell through to its raw-value branch. The frontend now maps the API 
values to localized
    labels, and the Tencent provider normalizes what the cloud returns into the 
same `SubscriptionFilterModes`
    vocabulary instead of passing vendor-specific strings through; 
`docs/api-spec.md` §6.4 documents the
    values.
    
    Also pins the mock and contract fixtures to values the backend can actually 
produce: `mock/audit.ts`
    no longer invents `UPDATE_ACL` / `RESTART_BROKER` (the real codes are 
`CREATE_ACL_RULE` and
    `RELOAD_PROXY_CONFIG`, verified against `AclService` / 
`ProxyAddressService` on trunk), and six API
    test fixtures drop values outside the backend's domain (`ttlStatus`, 
`status`, language, delivery
    status, alert level, topic type).
    
    `TencentInstanceProviderTest` 55 tests green, 0 checkstyle violations; 8 
frontend suites 110 tests
    green; prettier, eslint and `tsc -b` clean. Rebased onto today's trunk with 
the api-spec sections from
    #5205 / #5453 / #5438, the four import-toast assertions from #5238 and the 
`!silent` guards from #5121
    all intact.
    
    Honest note on coverage: the fixture changes cannot be pinned by a test - 
reverting all six leaves the
    suites green, because `clusterContract.test.ts` compares against itself and 
`opsService.test.ts` uses
    `arrayContaining`. They are kept because a fixture that the backend can 
never return silently weakens
    every case built on it; adding assertions just to make them 
mutation-visible would test the mock
    rather than the contract.
    
    Folded in #5265 and #5281 (same author).
---
 docs/api-spec.md                                   |   6 +-
 .../common/util/SubscriptionFilterModes.java       |   2 +-
 .../provider/tencent/TencentInstanceProvider.java  |   3 +-
 .../tencent/TencentInstanceProviderTest.java       |   7 +-
 web/src/api/clusterContract.test.ts                |   8 +-
 web/src/api/dlq.test.ts                            |   2 +-
 web/src/api/liteTopic.test.ts                      |   4 +-
 web/src/api/message.test.ts                        |   2 +-
 web/src/api/ops.test.ts                            |   4 +-
 web/src/api/producer.test.ts                       |   6 +-
 web/src/i18n/translations.ts                       |   1 +
 web/src/mock/audit.ts                              |  52 ++++----
 web/src/mock/consumers.ts                          | 146 ++++++++++-----------
 web/src/mock/dlq.ts                                |  18 +--
 .../pages/instance/__tests__/ConsumerPage.test.tsx | 114 ++++++++++------
 web/src/pages/instance/consumer.tsx                |  14 +-
 16 files changed, 214 insertions(+), 175 deletions(-)

diff --git a/docs/api-spec.md b/docs/api-spec.md
index 289ad8b4f..c411ab0a2 100644
--- a/docs/api-spec.md
+++ b/docs/api-spec.md
@@ -1174,9 +1174,9 @@ GET /api/groups/:name/subscriptions
 |------|------|------|
 | `topic` | `string` | Topic 名称 |
 | `expression` | `string` | 过滤表达式 |
-| `type` | `string` | 数据类型: `NORMAL` / `FIFO` / `DELAY` / `TRANSACTION` |
-| `filterMode` | `string` | 过滤模式: `Tag 过滤` / `SQL92 过滤` / `全量` |
-| `consistency` | `string` | 一致性: `一致` / `不一致` |
+| `type` | `string` | 订阅过滤表达式类型: `TAG` / `SQL92` / `CLASS_FILTER`(各 provider 
均原样透传 broker/OpenAPI 的表达式类型) |
+| `filterMode` | `string` | 过滤模式(由表达式类型归一化): `TAG` / `SQL` / `CLASS_FILTER` |
+| `consistency` | `string` | 订阅一致性。Apache 实例为 `consistent`,无在线客户端等无法判定时为 
`null`;云厂商实例为厂商返回值的字符串形式(如 Aliyun 的 `true` / `false`) |
 
 ### 6.5 创建消费组
 
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/common/util/SubscriptionFilterModes.java
 
b/server/src/main/java/org/apache/rocketmq/studio/common/util/SubscriptionFilterModes.java
index 20ae0a939..1c818c65c 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/common/util/SubscriptionFilterModes.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/common/util/SubscriptionFilterModes.java
@@ -18,7 +18,7 @@ package org.apache.rocketmq.studio.common.util;
 
 /**
  * Maps a subscription filter expression type to the studio {@code filterMode} 
display value.
- * Shared by the Apache and Aliyun providers so consumer-group subscription 
tables render the
+ * Shared by the Apache, Aliyun and Tencent providers so consumer-group 
subscription tables render the
  * same values across instance types: {@code SQL92} is normalized to {@code 
SQL}, {@code CLASS_FILTER}
  * is kept, and anything else (including a null/unknown type) defaults to 
{@code TAG}.
  */
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
index 75835d692..0338ab7ab 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
@@ -58,6 +58,7 @@ import 
org.apache.rocketmq.studio.common.domain.enums.TopicType;
 import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.common.util.Pagination;
 import org.apache.rocketmq.studio.common.util.SubscriptionConsistency;
+import org.apache.rocketmq.studio.common.util.SubscriptionFilterModes;
 import org.apache.rocketmq.studio.instance.InstanceRepository;
 import org.apache.rocketmq.studio.instance.InstanceVO;
 import org.apache.rocketmq.studio.instance.group.ConsumerGroupVO;
@@ -1072,7 +1073,7 @@ public class TencentInstanceProvider implements 
InstanceProvider {
                 .topic(subscription.getTopic())
                 .expression(subscription.getSubString())
                 .type(subscription.getExpressionType())
-                .filterMode(subscription.getExpressionType())
+                
.filterMode(SubscriptionFilterModes.fromExpressionType(subscription.getExpressionType()))
                 
.consistency(SubscriptionConsistency.fromCode(subscription.getConsistency()))
                 .build();
     }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
index feeacc993..704923775 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
@@ -729,7 +729,7 @@ class TencentInstanceProviderTest {
         SubscriptionData subscription = new SubscriptionData();
         subscription.setTopic("orders");
         subscription.setSubString("*");
-        subscription.setExpressionType("TAG");
+        subscription.setExpressionType("SQL92");
         subscription.setConsumerLag(42L);
         subscription.setConsistency(0L);
         DescribeTopicListByGroupResponse response = new 
DescribeTopicListByGroupResponse();
@@ -745,7 +745,10 @@ class TencentInstanceProviderTest {
         assertThat(subscriptions).hasSize(1);
         assertThat(subscriptions.get(0).getTopic()).isEqualTo("orders");
         assertThat(subscriptions.get(0).getExpression()).isEqualTo("*");
-        assertThat(subscriptions.get(0).getType()).isEqualTo("TAG");
+        // type keeps the raw broker expression type, while filterMode is 
normalized through
+        // SubscriptionFilterModes so all instance types render the same value 
domain.
+        assertThat(subscriptions.get(0).getType()).isEqualTo("SQL92");
+        assertThat(subscriptions.get(0).getFilterMode()).isEqualTo("SQL");
         
assertThat(subscriptions.get(0).getConsistency()).isEqualTo("consistent");
     }
 
diff --git a/web/src/api/clusterContract.test.ts 
b/web/src/api/clusterContract.test.ts
index 9a5bfeff4..7eae15fb3 100644
--- a/web/src/api/clusterContract.test.ts
+++ b/web/src/api/clusterContract.test.ts
@@ -28,13 +28,13 @@ const cluster: ClusterInfo = {
   nsClusterName: 'production',
   type: 'V5_PROXY_CLUSTER',
   endpoint: '127.0.0.1:9876',
-  status: 'RUNNING',
+  status: 'healthy',
   version: '5.3.0',
   brokers: [
     {
       name: 'broker-a',
       addr: '127.0.0.1:10911',
-      status: 'RUNNING',
+      status: 'running',
       tpsIn: 1,
       tpsOut: 2,
       diskUsage: 10,
@@ -44,13 +44,13 @@ const cluster: ClusterInfo = {
   proxies: [
     {
       addr: '127.0.0.1:8080',
-      status: 'RUNNING',
+      status: 'healthy',
       connections: 3,
       grpcPort: 8081,
       remotingPort: 8080,
     },
   ],
-  nameServers: [{ addr: '127.0.0.1:9876', status: 'RUNNING' }],
+  nameServers: [{ addr: '127.0.0.1:9876', status: 'healthy' }],
   config: {
     flushDiskType: 'ASYNC_FLUSH',
     autoCreateTopicEnable: true,
diff --git a/web/src/api/dlq.test.ts b/web/src/api/dlq.test.ts
index efe892d0f..5bc09555e 100644
--- a/web/src/api/dlq.test.ts
+++ b/web/src/api/dlq.test.ts
@@ -29,7 +29,7 @@ const group: DLQGroup = {
   messageCount: 3,
   lastEnqueueTime: '2026-07-17T00:00:00Z',
   retryCount: 16,
-  status: 'active',
+  status: 'ACTIVE',
 };
 
 describe('DLQ API', () => {
diff --git a/web/src/api/liteTopic.test.ts b/web/src/api/liteTopic.test.ts
index 9fac99c31..77e7d9805 100644
--- a/web/src/api/liteTopic.test.ts
+++ b/web/src/api/liteTopic.test.ts
@@ -38,7 +38,7 @@ const sampleItem: LiteTopicItem = {
   consumerCount: 3,
   totalBacklog: 1200,
   averageTTL: 3600,
-  ttlStatus: 'active',
+  ttlStatus: 'ACTIVE',
   lastActiveTime: Date.now(),
   sessionIds: ['sess-1', 'sess-2'],
 };
@@ -66,7 +66,7 @@ const sampleSession: LiteTopicSession = {
   lastActiveTime: Date.now(),
   ttl: 3600,
   ttlRemaining: 1800,
-  status: 'active',
+  status: 'ACTIVE',
   totalMessages: 5000,
   consumedMessages: 4800,
   pendingMessages: 200,
diff --git a/web/src/api/message.test.ts b/web/src/api/message.test.ts
index a190fbc0e..f3dc9bcec 100644
--- a/web/src/api/message.test.ts
+++ b/web/src/api/message.test.ts
@@ -118,7 +118,7 @@ describe('message API', () => {
       consumerStatus: [
         {
           group: 'cg-orders',
-          deliveryStatus: 'SUCCESS',
+          deliveryStatus: 'success',
           consumeTime: 1784246401000,
           retryCount: 0,
         },
diff --git a/web/src/api/ops.test.ts b/web/src/api/ops.test.ts
index 82b8e3bee..06a51b04d 100644
--- a/web/src/api/ops.test.ts
+++ b/web/src/api/ops.test.ts
@@ -299,7 +299,7 @@ describe('Ops API - System Alerts & Audit', () => {
       data: [
         {
           id: 1,
-          level: 'critical',
+          level: 'error',
           title: 'Disk Full',
           description: 'Disk usage > 95%',
           time: '2026-01-01',
@@ -308,7 +308,7 @@ describe('Ops API - System Alerts & Audit', () => {
       ],
     });
     const result = await listSystemAlerts();
-    expect(result[0].level).toBe('critical');
+    expect(result[0].level).toBe('error');
   });
 
   it('loads related system alerts for an event', async () => {
diff --git a/web/src/api/producer.test.ts b/web/src/api/producer.test.ts
index cd79af4bd..35945dd6f 100644
--- a/web/src/api/producer.test.ts
+++ b/web/src/api/producer.test.ts
@@ -97,13 +97,13 @@ describe('Producer API', () => {
         clientAddr: '192.168.1.10',
         topic: 'order-events',
         producerGroup: 'order-producer',
-        language: 'JAVA',
+        language: 'Java',
         versionDesc: '5.1.0',
       },
       {
         clientId: 'producer-2',
         clientAddr: '192.168.1.11',
-        language: 'JAVA',
+        language: 'Java',
         versionDesc: '5.1.0',
       },
     ];
@@ -136,7 +136,7 @@ describe('Producer API', () => {
               clientAddr: '192.168.1.10',
               topic: 'order-events',
               producerGroup: 'pg-order',
-              language: 'JAVA',
+              language: 'Java',
               versionDesc: '5.1.0',
             },
           ],
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index a1d09c4b3..08d7f4161 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -2308,6 +2308,7 @@ const translations: Record<string, Record<Lang, string>> 
= {
   'consumer.filterAll': { zh: '全量', en: 'Full' },
   'consumer.filterTag': { zh: 'Tag 过滤', en: 'Tag Filter' },
   'consumer.filterSql92': { zh: 'SQL92 过滤', en: 'SQL92 Filter' },
+  'consumer.filterClassFilter': { zh: 'Class 过滤', en: 'Class Filter' },
   'consumer.expression': { zh: '订阅表达式', en: 'Expression' },
   'consumer.viewDistribution': { zh: '查看分布', en: 'View Distribution' },
   'consumer.resetToTime': { zh: '重置到指定时间', en: 'Reset to Time' },
diff --git a/web/src/mock/audit.ts b/web/src/mock/audit.ts
index eb1ec1561..8f9a6c16d 100644
--- a/web/src/mock/audit.ts
+++ b/web/src/mock/audit.ts
@@ -68,11 +68,11 @@ export const mockAuditRecords = [
     id: 5,
     timestamp: '2026-07-03 07:22:19',
     operator: 'ops-wang',
-    operationType: 'UPDATE_ACL',
-    resourceType: 'TOPIC',
+    operationType: 'UPDATE_ACL_RULE',
+    resourceType: 'ACL_RULE',
     clusterId: 'prod-cn',
-    target: 'rmq-secure-topic',
-    detail: '为 Topic rmq-secure-topic 添加 ACL 规则,允许 user: readonly 读取',
+    target: '42',
+    detail: '更新 ACL 规则 42:允许 user 只读访问 rmq-secure-topic',
     ipAddress: '10.0.20.88',
     result: 'SUCCESS' as const,
   },
@@ -80,21 +80,21 @@ export const mockAuditRecords = [
     id: 6,
     timestamp: '2026-07-02 22:45:01',
     operator: 'system',
-    operationType: 'RESTART_BROKER',
-    resourceType: 'CLUSTER',
+    operationType: 'RESEND_DLQ',
+    resourceType: 'DLQ',
     clusterId: 'prod-hz',
-    target: 'broker-cn-hangzhou-02',
-    detail: '自动重启异常 Broker 节点 broker-cn-hangzhou-02',
+    target: 'order-group',
+    detail: '重发消费组 order-group 的死信消息',
     ipAddress: '10.0.1.2',
     result: 'FAILED' as const,
-    errorMessage: 'Broker did not become ready before the timeout',
+    errorMessage: 'DLQ resend timed out before completion',
   },
   {
     id: 7,
     timestamp: '2026-07-02 20:11:47',
     operator: 'ops-zhang',
     operationType: 'UPDATE_BROKER_CONFIG',
-    resourceType: 'CLUSTER',
+    resourceType: 'BROKER',
     clusterId: 'prod-sh',
     target: 'broker-cn-shanghai-01',
     detail: '修改 Broker broker-cn-shanghai-01 的 flushDiskType 为 ASYNC_FLUSH',
@@ -129,11 +129,11 @@ export const mockAuditRecords = [
     id: 10,
     timestamp: '2026-07-02 14:28:56',
     operator: 'ops-wang',
-    operationType: 'UPDATE_ACL',
-    resourceType: 'TOPIC',
+    operationType: 'UPDATE_ACL_RULE',
+    resourceType: 'ACL_RULE',
     clusterId: 'prod-cn',
-    target: 'rmq-admin-topic',
-    detail: '更新 Topic rmq-admin-topic 的 ACL 规则,移除 user: guest 的访问权限',
+    target: '43',
+    detail: '更新 ACL 规则 43:移除 user: guest 对 rmq-admin-topic 的访问权限',
     ipAddress: '10.0.20.88',
     result: 'SUCCESS' as const,
   },
@@ -154,11 +154,11 @@ export const mockAuditRecords = [
     id: 12,
     timestamp: '2026-07-02 10:15:42',
     operator: 'system',
-    operationType: 'RESTART_BROKER',
-    resourceType: 'CLUSTER',
+    operationType: 'ADD_PROXY_ADDRESS',
+    resourceType: 'PROXY',
     clusterId: 'prod-bj',
-    target: 'broker-cn-beijing-03',
-    detail: '手动重启 Broker 节点 broker-cn-beijing-03 以应用配置变更',
+    target: '10.0.1.1:8080',
+    detail: '为集群 prod-bj 添加 Proxy 地址 10.0.1.1:8080',
     ipAddress: '10.0.1.1',
     result: 'SUCCESS' as const,
   },
@@ -203,11 +203,11 @@ export const mockAuditRecords = [
     id: 16,
     timestamp: '2026-07-01 13:55:23',
     operator: 'ops-zhang',
-    operationType: 'UPDATE_ACL',
-    resourceType: 'TOPIC',
+    operationType: 'CREATE_ACL_RULE',
+    resourceType: 'ACL_RULE',
     clusterId: 'prod-cn',
-    target: 'rmq-billing-topic',
-    detail: '为 Topic rmq-billing-topic 创建新的 ACL 规则,限制仅 admin 组可写入',
+    target: '44',
+    detail: '创建 ACL 规则 44:principal=admin 仅可写入 Topic rmq-billing-topic',
     ipAddress: '10.0.15.22',
     result: 'SUCCESS' as const,
   },
@@ -227,11 +227,11 @@ export const mockAuditRecords = [
     id: 18,
     timestamp: '2026-07-01 08:19:44',
     operator: 'system',
-    operationType: 'RESTART_BROKER',
-    resourceType: 'CLUSTER',
+    operationType: 'RELOAD_PROXY_CONFIG',
+    resourceType: 'PROXY',
     clusterId: 'prod-sz',
-    target: 'broker-cn-shenzhen-01',
-    detail: '自动重启异常 Broker 节点 broker-cn-shenzhen-01,磁盘 IO 超时',
+    target: '10.0.30.12:8081',
+    detail: '重新加载集群 prod-sz 的 Proxy 10.0.30.12:8081 运行时配置',
     ipAddress: '10.0.1.3',
     result: 'SUCCESS' as const,
   },
diff --git a/web/src/mock/consumers.ts b/web/src/mock/consumers.ts
index 9275aa797..dd06ed058 100644
--- a/web/src/mock/consumers.ts
+++ b/web/src/mock/consumers.ts
@@ -36,7 +36,7 @@ export interface ConsumerGroup {
   onlineInstances: number;
   totalLag: number;
   subscribedTopics: string[];
-  subscriptionDataType: 'NORMAL' | 'FIFO' | 'DELAY' | 'TRANSACTION';
+  subscriptionDataType: 'NORMAL' | 'FIFO' | 'DELAY' | 'TRANSACTION' | 'LITE';
   deliveryOrderType?: 'PARTITON_ORDER' | 'MESSAGES_ORDER';
   retryMaxTimes: number;
   gmtCreate: string;
@@ -56,9 +56,9 @@ export interface QueueProgress {
 export interface SubscriptionEntry {
   topic: string;
   expression: string;
-  type: 'NORMAL' | 'FIFO' | 'DELAY' | 'TRANSACTION';
-  filterMode: 'Tag 过滤' | 'SQL92 过滤' | '全量';
-  consistency: '一致' | '不一致';
+  type: 'TAG' | 'SQL92' | 'CLASS_FILTER';
+  filterMode: 'TAG' | 'SQL' | 'CLASS_FILTER';
+  consistency: 'consistent' | 'inconsistent';
 }
 
 /* ─── Mock Data ─── */
@@ -901,181 +901,181 @@ export const mockSubscriptions: Record<string, 
SubscriptionEntry[]> = {
     {
       topic: 'order-create',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'order-status-change',
       expression: 'STATUS_PAID || STATUS_SHIPPED',
-      type: 'NORMAL',
-      filterMode: 'Tag 过滤',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'payment-callback',
       expression: '*',
-      type: 'TRANSACTION',
-      filterMode: '全量',
-      consistency: '不一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'inconsistent',
     },
   ],
   'cg-payment-callback': [
     {
       topic: 'payment-callback',
       expression: '*',
-      type: 'TRANSACTION',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'refund-event',
       expression: 'AMOUNT > 100',
-      type: 'TRANSACTION',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
   ],
   'cg-user-activity': [
     {
       topic: 'user-activity-log',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'user-profile-change',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
   ],
   'cg-inventory-sync': [
     {
       topic: 'inventory-sync',
       expression: '*',
-      type: 'FIFO',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'stock-alert',
       expression: 'LEVEL < 10',
-      type: 'FIFO',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
   ],
   'cg-log-collector': [
     {
       topic: 'app-log',
       expression: 'LEVEL in (ERROR, WARN)',
-      type: 'NORMAL',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
     {
       topic: 'access-log',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '不一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'inconsistent',
     },
     {
       topic: 'error-log',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
   ],
   'cg-notification-push': [
     {
       topic: 'notification-push',
       expression: 'PRIORITY >= 3',
-      type: 'DELAY',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
     {
       topic: 'sms-gateway',
       expression: '*',
-      type: 'DELAY',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
   ],
   'cg-ai-task-worker': [
     {
       topic: 'ai-task-dispatch',
       expression: 'MODEL in (GPT, CLAUDE)',
-      type: 'NORMAL',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
     {
       topic: 'model-inference-request',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
   ],
   'cg-metrics-aggregator': [
     {
       topic: 'metrics-raw',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'trace-span',
       expression: 'DURATION > 500',
-      type: 'NORMAL',
-      filterMode: 'SQL92 过滤',
-      consistency: '不一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'inconsistent',
     },
   ],
   'cg-risk-control': [
     {
       topic: 'transaction-event',
       expression: 'AMOUNT > 5000',
-      type: 'NORMAL',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
     {
       topic: 'login-event',
       expression: '*',
-      type: 'NORMAL',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'payment-callback',
       expression: 'RISK_LEVEL >= 2',
-      type: 'NORMAL',
-      filterMode: 'SQL92 过滤',
-      consistency: '一致',
+      type: 'SQL92',
+      filterMode: 'SQL',
+      consistency: 'consistent',
     },
   ],
   'cg-data-sync': [
     {
       topic: 'binlog-event',
       expression: '*',
-      type: 'FIFO',
-      filterMode: '全量',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
     {
       topic: 'schema-change',
       expression: 'DDL',
-      type: 'FIFO',
-      filterMode: 'Tag 过滤',
-      consistency: '一致',
+      type: 'TAG',
+      filterMode: 'TAG',
+      consistency: 'consistent',
     },
   ],
 };
diff --git a/web/src/mock/dlq.ts b/web/src/mock/dlq.ts
index c46a7501e..13aa98a7c 100644
--- a/web/src/mock/dlq.ts
+++ b/web/src/mock/dlq.ts
@@ -21,7 +21,7 @@ export interface DLQGroup {
   messageCount: number;
   lastEnqueueTime: string;
   retryCount: number;
-  status: 'active' | 'empty';
+  status: 'ACTIVE' | 'EMPTY' | 'UNAVAILABLE';
 }
 
 export const mockDLQGroups: DLQGroup[] = [
@@ -31,7 +31,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 23,
     lastEnqueueTime: '2026-07-01T14:32:18.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-payment-handler',
@@ -39,7 +39,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 8,
     lastEnqueueTime: '2026-07-01T15:10:45.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-notification',
@@ -47,7 +47,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 156,
     lastEnqueueTime: '2026-07-01T16:05:22.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-inventory-sync',
@@ -55,7 +55,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 3,
     lastEnqueueTime: '2026-07-01T12:48:33.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-analytics',
@@ -63,7 +63,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 42,
     lastEnqueueTime: '2026-07-01T15:55:10.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-user-tracking',
@@ -71,7 +71,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 0,
     lastEnqueueTime: '2026-06-28T09:12:00.000Z',
     retryCount: 16,
-    status: 'empty',
+    status: 'EMPTY',
   },
   {
     groupName: 'cg-sms-gateway',
@@ -79,7 +79,7 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 67,
     lastEnqueueTime: '2026-07-01T16:20:55.000Z',
     retryCount: 16,
-    status: 'active',
+    status: 'ACTIVE',
   },
   {
     groupName: 'cg-email-service',
@@ -87,6 +87,6 @@ export const mockDLQGroups: DLQGroup[] = [
     messageCount: 0,
     lastEnqueueTime: '2026-06-30T18:05:12.000Z',
     retryCount: 16,
-    status: 'empty',
+    status: 'EMPTY',
   },
 ];
diff --git a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx 
b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
index cc31f0e72..859bb90f8 100644
--- a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
+++ b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
@@ -229,9 +229,9 @@ describe('Consumer page', () => {
       {
         topic: 'remote-topic',
         expression: '*',
-        type: 'NORMAL',
-        filterMode: '全量',
-        consistency: '一致',
+        type: 'TAG',
+        filterMode: 'TAG',
+        consistency: 'consistent',
       },
     ]);
     vi.mocked(consumerService.previewConsumerOffsetReset).mockResolvedValue({
@@ -750,6 +750,36 @@ describe('Consumer page', () => {
     expect(cells.filter((cell) => cell === '-')).toHaveLength(2);
   });
 
+  it('renders the filter modes the API returns as localized labels', async () 
=> {
+    const user = userEvent.setup();
+    vi.mocked(consumerService.getConsumerSubscriptions).mockResolvedValue([
+      {
+        topic: 'remote-topic',
+        expression: 'tagA',
+        type: 'TAG',
+        filterMode: 'TAG',
+        consistency: 'consistent',
+      },
+      {
+        topic: 'sql-topic',
+        expression: 'a > 1',
+        type: 'SQL92',
+        filterMode: 'SQL',
+        consistency: 'consistent',
+      },
+    ]);
+    renderWithProviders(<ConsumerPage />);
+
+    await user.click(await screen.findByRole('button', { name: /详情/ }));
+
+    expect(await screen.findByText('Tag 过滤')).toBeInTheDocument();
+    expect(screen.getByText('SQL92 过滤')).toBeInTheDocument();
+    // The providers normalize the broker expression types to TAG / SQL / 
CLASS_FILTER
+    // (SubscriptionFilterModes), so the raw codes must not leak into the 
table.
+    expect(screen.queryByText('TAG')).not.toBeInTheDocument();
+    expect(screen.queryByText('SQL')).not.toBeInTheDocument();
+  });
+
   it('shows group health diagnostics from subscriptions, progress and 
clients', async () => {
     const riskyGroup: ConsumerGroup = {
       ...group,
@@ -780,9 +810,9 @@ describe('Consumer page', () => {
       {
         topic: 'remote-topic',
         expression: 'tagA',
-        type: 'NORMAL',
-        filterMode: 'Tag 过滤',
-        consistency: '不一致',
+        type: 'TAG',
+        filterMode: 'TAG',
+        consistency: 'inconsistent',
       },
     ]);
     vi.mocked(consumerService.getConsumerProgress).mockResolvedValue([
@@ -822,16 +852,16 @@ describe('Consumer page', () => {
       {
         topic: 'remote-topic',
         expression: '*',
-        type: 'NORMAL',
-        filterMode: '全量',
-        consistency: '一致',
+        type: 'TAG',
+        filterMode: 'TAG',
+        consistency: 'consistent',
       },
       {
         topic: '%RETRY%remote-cg',
         expression: '*',
-        type: 'RETRY',
-        filterMode: '全量',
-        consistency: '一致',
+        type: 'TAG',
+        filterMode: 'TAG',
+        consistency: 'consistent',
       },
     ]);
     const user = userEvent.setup({ pointerEventsCheck: 0 });
@@ -1296,15 +1326,15 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
         {
           topic: 'stale-topic',
           expression: 'important',
-          type: 'NORMAL',
-          filterMode: 'Tag 过滤',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'inconsistent',
         },
       ])
@@ -1312,15 +1342,15 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
         {
           topic: 'stale-topic',
           expression: 'important',
-          type: 'NORMAL',
-          filterMode: 'Tag 过滤',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1348,15 +1378,15 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
         {
           topic: 'stale-topic',
           expression: 'important',
-          type: 'NORMAL',
-          filterMode: 'Tag 过滤',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'inconsistent',
         },
       ])
@@ -1364,15 +1394,15 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
         {
           topic: 'stale-topic',
           expression: 'important',
-          type: 'NORMAL',
-          filterMode: 'Tag 过滤',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1414,15 +1444,15 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
         {
           topic: 'new-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1435,8 +1465,8 @@ describe('Consumer page', () => {
         {
           topic: 'stale-topic',
           expression: 'important',
-          type: 'NORMAL',
-          filterMode: 'Tag 过滤',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'inconsistent',
         },
       ]);
@@ -1457,8 +1487,8 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1480,8 +1510,8 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1492,8 +1522,8 @@ describe('Consumer page', () => {
         {
           topic: 'remote-topic',
           expression: '*',
-          type: 'NORMAL',
-          filterMode: '全量',
+          type: 'TAG',
+          filterMode: 'TAG',
           consistency: 'consistent',
         },
       ]);
@@ -1513,8 +1543,8 @@ describe('Consumer page', () => {
       {
         topic: 'unknown-topic',
         expression: '*',
-        type: 'NORMAL',
-        filterMode: '全量',
+        type: 'TAG',
+        filterMode: 'TAG',
         consistency: 'UNKNOWN',
       },
     ]);
diff --git a/web/src/pages/instance/consumer.tsx 
b/web/src/pages/instance/consumer.tsx
index 971fcd6ee..dd5624c4e 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -1112,12 +1112,16 @@ const ConsumerPageContent = ({
       key: 'filterMode',
       width: 120,
       render: (mode: string) => {
-        const colorMap: Record<string, string> = {
-          全量: 'default',
-          'Tag 过滤': 'blue',
-          'SQL92 过滤': 'purple',
+        // The providers normalize the broker expression types to TAG / SQL / 
CLASS_FILTER
+        // (SubscriptionFilterModes.fromExpressionType); map those codes to 
labels instead of
+        // echoing them into the table.
+        const meta: Record<string, { color: string; labelKey: string }> = {
+          TAG: { color: 'blue', labelKey: 'consumer.filterTag' },
+          SQL: { color: 'purple', labelKey: 'consumer.filterSql92' },
+          CLASS_FILTER: { color: 'gold', labelKey: 
'consumer.filterClassFilter' },
         };
-        return <Tag color={colorMap[mode] || 'default'}>{mode}</Tag>;
+        const entry = meta[mode];
+        return <Tag color={entry?.color ?? 'default'}>{entry ? 
t(entry.labelKey) : mode}</Tag>;
       },
     },
     {

Reply via email to