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>;
},
},
{