You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by li...@apache.org on 2026/07/28 10:10:47 UTC

(rocketmq-dashboard) 01/03: feat: add core cluster and topic model DTOs (#525)

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

commit c48094cba3cb0e2dcd02cb7eeddf53132eacaa96
Author: zhaohai <33...@users.noreply.github.com>
AuthorDate: Tue Jul 28 18:07:53 2026 +0800

    feat: add core cluster and topic model DTOs (#525)
    
    * [studio] feat: add core cluster and topic model DTOs
    
    Add foundational model classes for cluster topology, capabilities,
    topic types, consumer groups, and client connections. These POJOs
    serve as the data contract for all subsequent service and controller
    layers.
    
    * chore: remove empty javadoc comments
    
    * [Studio] refactor: remove Lombok, rename package to org.apache.rocketmq.studio, move under server/src
    
    * [Studio] refactor: move models to server/src, exclude files with missing dependencies
---
 docs/contribution-guide.md                         |  41 +++
 docs/conversation-record-20260722.md               | 240 +++++++++++++
 docs/progress-20260728.md                          |  81 +++++
 .../apache/rocketmq/studio/model/ACLPolicy.java    | 197 +++++++++++
 .../org/apache/rocketmq/studio/model/ACLUser.java  | 141 ++++++++
 .../rocketmq/studio/model/AccessControlList.java   | 133 ++++++++
 .../rocketmq/studio/model/Acl2PolicyContext.java   | 320 ++++++++++++++++++
 .../rocketmq/studio/model/ClientInstance.java      | 373 +++++++++++++++++++++
 .../rocketmq/studio/model/ClusterCapability.java   | 150 +++++++++
 .../rocketmq/studio/model/ClusterTopology.java     | 256 ++++++++++++++
 .../rocketmq/studio/model/ConsumerGroupInfo.java   | 210 ++++++++++++
 .../studio/model/ConsumerMonitorConfig.java        |  46 +++
 .../org/apache/rocketmq/studio/model/Entry.java    |  61 ++++
 .../rocketmq/studio/model/LiteTopicQuota.java      | 130 +++++++
 .../rocketmq/studio/model/LiteTopicSession.java    | 223 ++++++++++++
 .../rocketmq/studio/model/LiteTopicSummary.java    | 163 +++++++++
 .../apache/rocketmq/studio/model/LoginInfo.java    |  39 +++
 .../apache/rocketmq/studio/model/LoginResult.java  |  54 +++
 .../apache/rocketmq/studio/model/MessageInfo.java  | 144 ++++++++
 .../studio/model/MetricsDataSourceConfig.java      | 182 ++++++++++
 .../rocketmq/studio/model/MetricsHealthResult.java |  87 +++++
 .../org/apache/rocketmq/studio/model/Policy.java   |  42 +++
 .../rocketmq/studio/model/PolicyRequest.java       |  60 ++++
 .../rocketmq/studio/model/SubscriptionInfo.java    | 130 +++++++
 .../apache/rocketmq/studio/model/TopicInfo.java    | 226 +++++++++++++
 .../apache/rocketmq/studio/model/TopicType.java    |  69 ++++
 .../org/apache/rocketmq/studio/model/User.java     |  83 +++++
 .../org/apache/rocketmq/studio/model/UserInfo.java |  67 ++++
 .../model/request/ArchitectureSwitchRequest.java   |  72 ++++
 .../studio/model/request/MessageQuery.java         |  82 +++++
 .../model/request/MetricsDataSourceRequest.java    | 211 ++++++++++++
 .../studio/model/request/ResetOffsetRequest.java   |  58 ++++
 .../model/request/SendTopicMessageRequest.java     |  66 ++++
 .../studio/model/request/TopicTypeList.java        |  45 +++
 .../studio/model/request/TopicTypeMeta.java        |  38 +++
 .../studio/model/request/UserCreateRequest.java    |  49 +++
 .../studio/model/request/UserInfoParam.java        |  58 ++++
 .../studio/model/request/UserUpdateRequest.java    |  49 +++
 .../studio/model/trace/MessageTraceGraph.java      |  52 +++
 .../studio/model/trace/MessageTraceStatusEnum.java |  35 ++
 .../rocketmq/studio/model/trace/ProducerNode.java  |  95 ++++++
 .../studio/model/trace/SubscriptionNode.java       |  41 +++
 .../rocketmq/studio/model/trace/TraceNode.java     | 130 +++++++
 .../rocketmq/dashboard/model/ClientInstance.java   | 373 +++++++++++++++++++++
 .../dashboard/model/ClusterCapability.java         | 150 +++++++++
 .../rocketmq/dashboard/model/ClusterTopology.java  | 197 +++++++++++
 .../rocketmq/dashboard/model/ConnectionInfo.java   |  54 +++
 .../dashboard/model/ConsumerGroupInfo.java         | 210 ++++++++++++
 .../rocketmq/dashboard/model/GroupConsumeInfo.java | 125 +++++++
 .../rocketmq/dashboard/model/SubscriptionInfo.java | 130 +++++++
 .../dashboard/model/TopicConsumerInfo.java         |  62 ++++
 .../apache/rocketmq/dashboard/model/TopicInfo.java | 226 +++++++++++++
 .../apache/rocketmq/dashboard/model/TopicType.java |  69 ++++
 53 files changed, 6625 insertions(+)

diff --git a/docs/contribution-guide.md b/docs/contribution-guide.md
new file mode 100644
index 00000000..87c313dc
--- /dev/null
+++ b/docs/contribution-guide.md
@@ -0,0 +1,41 @@
+# RocketMQ Studio 贡献说明
+
+## 活动简介
+
+**RocketMQ 5.0 Studio:全新的 AI-Native 管控平台**
+
+RocketMQ 5.0 新增了云原生弹性架构、多语言 gRPC 接入、Lite Topic 等核心特性,完成了向 AI-Native 异步通信引擎的范式升级。本次社区共建活动旨在打造全新的 AI-Native 管控平台,由于任务较为复杂,拆分为三个方向:
+
+- **赛道一:管控方向(AI-Native 统一管控平台)** — 打造 RocketMQ 5.0 Studio 核心可视化前后端服务,一站式实现多集群、多架构、多云环境的统一管控。
+- **赛道二:协议升级(Proxy Admin 管控能力升级)** — 构建 Proxy Admin gRPC 协议标准,将客户端连接管理与运行时诊断能力全面标准化开放。
+- **赛道三:AI Native(大模型原生集成)** — 借助 Skill + CLI 标准化能力封装,资源管理与运维操作可被 LLM 直接理解与调用。
+
+## 参与方式
+
+本次活动的形式以**开源贡献**为主,不同于天池、LeetCode、HackerRank 等以排名和积分为导向的竞赛平台:
+
+- **按顺序合并**:贡献以 GitHub Issue 和 PR 为单位,按照提交顺序进行 Review 和合并,先到先得。没有额外的评分系统或排名机制,每一份有效贡献经过 Code Review 通过后即可合入代码库。
+- **贡献记录**:所有被合并的 PR 将永久记录在 Apache 项目历史中,成为你开源履历的一部分。
+
+---
+
+## 赛道说明
+
+### 赛道一:管控方向(AI-Native 统一管控平台)
+
+项目已在 rocketmq-studio 上实现了一个前后端模板,技术栈选用 TypeScript + Java(Spring Boot 3.5 + Java 21),采用较为通用的后端管控平台架构,适合不同背景的开发者参与。
+贡献者可直接提交 Issue 和 PR。建议每个改动控制在 **15 个文件以内**,以**功能模块为粒度**,支持前后端一起修改。每个 PR 应聚焦于单个功能模块的完整实现,确保可独立验证、独立回滚。
+
+### 赛道二:协议升级(Proxy Admin 管控能力升级)
+
+社区将提供由多位社区成员共同讨论制定的 gRPC admin proto 定义(不含具体实现),贡献者需基于该 proto 完成实现代码,代码需提交到 **RocketMQ 主仓库**(apache/rocketmq)的 **proxy 模块**下。
+该赛道涉及底层架构设计与接口契约变更,**请先发起 Issue 讨论设计方案**,经过讨论后再提交 PR。
+
+### 赛道三:AI Native(大模型原生集成)
+
+本赛道将 RocketMQ 的资源管理与运维操作封装为标准化的 Skill,使 LLM 能够直接理解和调用这些能力。客户端 CLI 支持使用任意语言实现,但需提交设计方案评审;服务端 MCP 基于赛道一的 Java 后端,通过 remoting 协议管理对应的 Broker,所有复杂逻辑(鉴权、请求审计、LLM 调用)均在此集中处理。
+该赛道涉及 Skill 规范与接口契约设计,**请先发起 Issue 讨论方案**,经过讨论后再提交 PR。
+
+---
+
+如有任何问题,可以在 GitHub Issue 下留言讨论,也可以通过微信群或钉钉群与我们联系。
diff --git a/docs/conversation-record-20260722.md b/docs/conversation-record-20260722.md
new file mode 100644
index 00000000..5b22d70d
--- /dev/null
+++ b/docs/conversation-record-20260722.md
@@ -0,0 +1,240 @@
+# RocketMQ Studio PR 批量整合 — 对话记录与提示词 (2026-07-22)
+
+> 本文件为会话记录/复用模板,**未提交**(仅本地保存,方便下次使用)。
+> 目标仓库:`apache/rocketmq-dashboard`,集成分支:`rocketmq-studio`。
+> 作者/committer:`lizhimins`(有 write 权限,仓库仅允许 squash 合并)。
+
+## 一、本次会话用到的关键提示词(按顺序)
+
+1. 讨论 `project/rocketmq-studio`,先同步远程,它有几个 git remote
+2. 这个项目配置了多少天关闭无效的 issue 和 PR,为什么不生效?
+3. 先在 master 分支上改,每天 200,帮我提交 PR(stale 工作流 operations-per-run)
+4. 先不同步到 studio 分支
+5. apache 上积攒了一堆 studio 的 PR,筛选近一个月,先都获取,看每个改了什么、多少文件
+6. 切换到 apache 的 rocketmq-studio 分支
+7. 看 open 的 45 个,整理 markdown 报告到 docs 下
+8. 评估 PR #432(Prometheus range query adapter),把合并后的分支用 docker compose 部署到 47.83.18.255(密码 Terrances123!)
+9. 把原来的(占端口的容器)关了
+10. 合并这个 commit,注意 commit msg 符合历史规范,先给我确认
+11. 我是 committer,可以直接用 gh 合并吗 → 执行
+12. 同步本地、清理临时分支
+13. 分析 zhaohai666 的提交(11 前端页面 + 3 后端)
+14. 在本地按顺序把所有前端 PR 一个个合进来,模拟正式仓库操作,再部署看效果
+15. 把 #478 Namespace 关了(当前版本不支持 Namespace),其余按顺序合并
+16. 按这个顺序合入 apache 的 PR → 逐个处理冲突并合并(用 merge base 进 PR 分支,非 force push)
+17. 处理 zhaohai666 后端改动:先 fetch,重新评估 → 发现已被主干覆盖,关闭 #455/#456/#457
+18. 分析最后一个同学 Loyal-Young 的 PR(很多)
+19. 把他的改动都合到本地,拆成约 3 个 commit,关联到他原始 PR、改写这 3 个 PR,关闭其余;作者只写他
+20. 先部署到远程 → 迭代修构建错误 → 执行 apache 三组拆分
+21. 重新部署 / 删除本地无效分支 / 保存对话到 docs(不提交)
+
+## 二、稳定可复用的操作套路
+
+### A. Stale 工作流不生效的排查
+- `gh run list --workflow=stale.yml` 看是否在跑;`gh run view <id>` 看注解。
+- 常见根因:`operations-per-run` 默认 30,每次只能处理约 15 个 → 积压清不完。调大到 200。
+
+### B. 单个 PR 评审 + 远程部署(pr-review skill 流程)
+- `gh pr view/diff` 取元信息与 diff;标题规范校验;拉关联 Issue。
+- 本地 `gh pr checkout` → 远程 tar over ssh → `docker compose up -d --build`。
+- 远程 47.83.18.255 = podman 4.9(`docker`=podman,有 `docker compose`)。端口 6789(web)/8888(server)。
+- 健康检查:`curl localhost:6789/`、`/actuator/health`;公网需安全组放行 6789。
+
+### C. committer 直接合并(squash)
+```
+gh pr merge <N> --repo apache/rocketmq-dashboard --squash \
+  --subject "feat: xxx (#N)" --body "..."
+```
+- 标题规范化为 `type: desc (#N)`(去掉 `[Studio]` 前缀,符合 rocketmq-studio 历史)。
+
+### D. 逐个把冲突 PR 合入 apache(不 force push)
+对每个 PR:`gh pr checkout` → `git merge apache/rocketmq-studio`(把 base 并入 PR 分支)→ 解决冲突(多为 App.tsx 路由/import、translations.ts 文案的加性冲突,保留双方 + i18n key 去重)→ 推回贡献者 fork 分支(需 `maintainerCanModify=true`)→ squash 合并。
+- **注意**:https push 到 github 常超时,改用 SSH:`git push git@github.com:<owner>/rocketmq-dashboard.git HEAD:<headRef>`。
+
+### E. 把某贡献者多个 PR 整合成少数几个 commit(本次核心)
+1. 本地建 `integrate/<author>` 分支,从 `rocketmq-studio` 起。
+2. 拉取所有 PR head,按 PR 号顺序 `git merge --squash` 逐个合并;冲突谨慎解决(真实代码冲突不能盲目"保留双方")。
+3. **有 husky+eslint pre-commit hook** 会挡住语法错误;**tsc(远程/本地 npm ci 后 build)** 会挡住类型错误/重复声明/缺依赖。二者都要过。
+4. 按主题把改动文件分成 N 组;**注意文件间跨组依赖**(如 MainLayout 依赖 navigationSearch.ts;client.ts 依赖 config.ts 的 API_BASE_URL)——用本地 `npm run build` 逐组累积校验,缺依赖就把文件并入该组。
+5. 对每个容器 PR:`gh pr checkout` → `git merge -s ours apache/rocketmq-studio`(让 base 成祖先,避免 force push)→ `git read-tree --reset -u apache/rocketmq-studio`(树重置为 base)→ `git checkout integrate/<author> -- <该组文件>` → `git commit --author="<贡献者>"` → SSH 推送 → squash 合并。
+6. 其余 PR 关闭,注明整合去向。
+
+## 三、本次踩过的坑(下次避免)
+
+1. **`while read` 跳过无尾换行的最后一行**:`'\n'.join()` 生成的文件列表没有尾换行,`while IFS= read -r f` 会漏掉每个列表最后一个文件。导致每组各漏一个文件(topicService/authStore/vite.config),事后用 #490 补齐。→ 生成列表时加尾换行,或用 `for f in $(cat list)`。
+2. **merge-tree 判断"冗余"不可靠**:有冲突时 `git merge-tree` 的净变化统计会误判为空。判断某 PR 是否已被主干覆盖,应比对"该 PR 自有文件"的实际内容 diff。
+3. **base 已覆盖**:zhaohai666 的 3 个后端 PR(#455/#456/#457)内容已由主干初始导入提交(`49bc4419`/`b773716a`)覆盖;#455 合并还会把 GlobalExceptionHandler 退回旧版。→ 合并前先核对文件是否已在 base。
+4. **"保留双方"仅适用于加性冲突**(路由、i18n key、import 并集)。对同函数不同实现的真实冲突会产生重复声明/重复 key,被 eslint/tsc 挡下。i18n 需去重;重复函数需删一个;对象缺字段要补齐(如 createConsumerGroup 缺 updatedAt/delaySeconds)。
+5. **本地 build 假阳性**:本地 node_modules 缺 devDeps(@testing-library)会报一堆假错;`npm ci` 后与远程一致,才是可靠 oracle。
+6. **vite.config.ts 用 `process.cwd()`** 需 @types/node;改用 `loadEnv(mode, '.', '')` 免依赖。
+7. **端口 6789 被遗留 rootlessport 占用**:podman 容器删除后端口转发器残留,`kill <pid>` 释放。
+8. **https push 超时**:github https 不稳,统一用 SSH 通道推送。
+
+## 四、本次最终结果
+
+- **Stale 工作流**:#489 合入 master,`operations-per-run: 200`。
+- **#432** Prometheus adapter:squash 合入 rocketmq-studio。
+- **zhaohai666 前端**:#473–#483 共 10 个 squash 合入;#478 Namespace 关闭。
+- **zhaohai666 后端**:#455/#456/#457 关闭(已被主干覆盖)。
+- **Loyal-Young**:29 个前端 PR 整合为 3 个容器 PR(#445 API 对接 / #462 服务能力+交互 / #465 偏好+无障碍+配置)+ #490 补充提交,作者均为 `G17 ShoWang24`;其余 26 个 + #467 关闭。
+- apache `rocketmq-studio` 应用代码 == 本地已验证构建通过的集成结果。
+
+## 五、环境速查
+
+- 远程开发机:`root@47.83.18.255`(密码 `Terrances123!`),podman + docker compose,4C/15G。
+- 部署:源码 tar over ssh → `/opt/rocketmq-studio` → `deploy/docker-compose.yml` → `docker compose up -d --build`。
+- Loyal-Young git author:`G17 ShoWang24 <17...@qq.com>`。
+
+## 六、待合并 PR 清单与评审 (2026-07-23)
+
+> 截至 2026-07-23,`rocketmq-studio` 分支共 19 个 open PR,全部 MERGEABLE。
+> 评审基于 `pr-review` skill 标准进行静态代码审查(未本地构建),涵盖标题规范、代码质量、测试覆盖、安全风险四个维度。
+
+| PR | 作者 | 中文描述 | 文件 | +/- | 标题 | 代码 | 测试 | 安全 | 评审结论 |
+|----|------|----------|------|-----|------|------|------|------|----------|
+| #492 | @Kris20030907 | 修复前端登录响应类型与后端 LoginVO 契约不匹配,将 username/role 改为嵌套 user.username/admin 结构 | 3 | +14/-8 | ✅ | ✅ | ✅ | ✅ | **Approve** |
+| #494 | @majialoong | 修复 Nginx API 代理因硬编码 DNS resolver 导致的失败,改用容器运行时动态解析 | 2 | +4/-2 | ⚠️ "Use" 应小写 "use" | ✅ | ⚠️ 无单测 | ✅ | **Approve**(修正标题后) |
+| #495 | @zhaohai666 | 将 UI 硬编码中文文本迁移为 i18n labelKey 模式,新增约 280 条翻译条目并修复重复 key 编译错误 | 10 | +529/-74 | ✅ | ✅ | ✅ 18 用例 | ✅ | **Approve**:aria-label 仍硬编码中文前缀,squash 时顺带修 |
+| #496 | @zhaohai666 | 对齐前端 API 路径与后端 Controller 端点,补全 format.ts 中 6 个 TODO 格式化函数 | 8 | +401/-25 | ✅ | ❌ auth 路径改为 `/login/login.do` 但后端是 `/api/auth`,合并会打挂登录;createInstance 仅传 proxyAddr;deleteInstance 仍走 mock | ✅ 34 用例 | ✅ | **Request Changes**:auth API 路径与后端不匹配(严重);API 请求体不完整 |
+| #497 | @zhaohai666 | 升级 MiniLine 图表组件(平滑曲线/渐变/响应式),重新设计首页布局,新增 388 行全局 CSS | 6 | +686/-157 | ✅ | ⚠️ `_lineId` 模块级可变状态应改 useId();CSS 大量 !important | ✅ 12 用例 | ✅ | **Request Changes**:MiniLine ID 生成方案;CSS !important 维护性差 |
+| #498 | @Aias00 | 修复前端基线测试中断言与后端 PageResult 契约及 i18n 标签不一致的问题 | 2 | +7/-3 | ✅ | ✅ | ✅ | ✅ | **Approve** |
+| #499 | @Aias00 | 将部署脚本中日志/错误辅助函数初始化移到配置校验之前,修复 err() 未定义的 bug | 1 | +12/-12 | ✅ | ✅ | ⚠️ 无单测(shell 可接受) | ✅ | **Approve** |
+| #500 | @Aias00 | 在 CI 中新增前端 Docker 镜像构建任务,覆盖 Dockerfile/nginx 部署路径 | 1 | +10/-0 | ✅ | ✅ | ✅ | ✅ | **Approve** |
+| #501 | @Aias00 | 修复 Producer 页面 topic 列表读取逻辑,兼容 Studio API 新格式和旧格式 | 2 | +23/-3 | ✅ | ✅ 向后兼容完善 | ✅ | ✅ | **Approve** |
+| #502 | @Aias00 | 为 LiteTopic 页面添加后端 stub 接口(列表/会话/TTL/配额/能力检查) | 9 | +566/-0 | ⚠️ 应为 `feat` 非 `fix` | ⚠️ 全硬编码 stub 数据 | ✅ | ✅ | **Comment**:type 改 feat;标注 stub 替换计划 |
+| #504 | @Aias00 | 为 Ops 页面添加 NameServer 地址管理、VIP Channel 和 TLS 开关的后端接口 | 8 | +430/-0 | ⚠️ 应为 `feat` 非 `fix` | ⚠️ 内存存储重启丢失;namesrvAddr 缺格式校验 | ✅ | ✅ | **Comment**:type 改 feat;增加 host:port 正则校验 |
+| #505 | @Aias00 | 新增 Proxy 地址列表和添加地址的兼容接口,支持前端表单提交 | 5 | +262/-0 | ⚠️ 应为 `feat` 非 `fix` | ⚠️ 内存 LinkedHashSet 重启丢失;默认值硬编码 | ✅ | ✅ | **Comment**:type 改 feat;默认地址提取为配置项 |
+| #506 | @Aias00 | 为 LLM 设置页面添加配置读写、连通性测试和模型列表的后端兼容接口 | 8 | +611/-0 | ⚠️ 应为 `feat` 非 `fix` | ⚠️ Controller 未用 Result<> 包装 | ✅ | ⚠️ GET 返回 apiKey 明文 | **Request Changes**:apiKey 响应脱敏;Controller 格式统一;type 改 feat |
+| #507 | @Aias00 | 新增告警规则 Prometheus YAML 导出接口,无规则时返回默认 RocketMQ 告警模板 | 5 | +311/-0 | ⚠️ 应为 `feat` 非 `fix` | ⚠️ StringBuilder 手工拼 YAML 有注入风险;severity 固定为 warning | ✅ | ✅ | **Comment**:type 改 feat;建议引入 SnakeYAML 库生成 YAML |
+| #509 | @Kris20030907 | 修复创建数据源时 key 为 null 导致 ConcurrentHashMap NPE,服务层自动生成 UUID | 3 | +72/-6 | ✅ | ✅ | ✅ 全面 | ✅ | **Approve** |
+| #510 | @Aias00 | 新增 Producer 连接查询接口,按 topic 和 producerGroup 过滤并返回兼容格式 | 8 | +344/-0 | ⚠️ 应为 `feat` 非 `fix` | ✅ 逻辑清晰 | ✅ | ✅ | **Approve**(改 type 后) |
+| #512 | @yx9o | 修复通用设置接口不再返回 API Key 明文,改用 apiKeyConfigured 布尔值标识 | 10 | +318/-31 | ✅ | ✅ DTO/VO 分离,@Valid 完整 | ✅ 全面 | ✅ | **Approve** |
+| #514 | @PiliLily | 引入版本化 YAML 工具目录及两个只读 L1 工具处理器(cluster.list/capabilities) | 22 | +1758/-36 | ✅ | ✅ 架构严谨,不可变/schema/fail-fast 到位 | ✅ 720+ 行测试 | ✅ | **Approve** |
+| #515 | @PiliLily | 定义集群能力三态契约及 configured-default 解析器,暴露 capabilities 端点 | 7 | +773/-13 | ✅ | ✅ | ✅ 296 行测试 | ✅ | **Comment (DRAFT)**:代码质量达标,待移除 DRAFT 标记;注意与 #514 功能重叠需协调 |
+
+### 评审统计
+
+| 结论 | 数量 | PR 编号 |
+|------|------|---------|
+| Approve | 11 | #492, #494, #495, #498, #499, #500, #501, #509, #510, #512, #514 |
+| Request Changes | 3 | #496, #497, #506 |
+| Comment | 5 | #502, #504, #505, #507, #515 (DRAFT) |
+
+### 高频问题
+
+1. **commit type `fix` 误标**(#502/#504/#505/#506/#507/#510):新增功能应使用 `feat` 而非 `fix`,squash 时统一修正
+2. **stub 数据遗留**(#502/#504/#505):多个 PR 使用内存硬编码 stub,需在 PR body 或 TODO 中标注替换计划
+3. **apiKey 明文返回**(#506):GET /api/llm/config 将 apiKey 原样返回前端,属安全隐患
+
+### 按作者汇总与合并建议
+
+| 作者 | PR 数 | Approve | 待修改 | 建议 |
+|------|-------|---------|--------|------|
+| @Aias00 | 10 | #498/#499/#500/#501/#510 | #506(apiKey 脱敏)其余 5 个仅 type 标注 | #506 单独处理;其余可整合为 2 个容器 PR(后端 endpoint + 前端/CI),squash 时统一 type 为 feat |
+| @zhaohai666 | 3 | #495 | #496(API 请求体不完整)、#497(MiniLine/CSS 质量) | #495 可直接 squash 合并;#496/#497 需作者修复代码问题后可整合为 1 个容器 PR |
+| @Kris20030907 | 2 | 全部 | — | 体积小,可直接 squash 合并 |
+| @PiliLily | 2 | #514 | #515 DRAFT 暂缓 | #514 直接合并;#515 等 DRAFT 移除后再审 |
+| @majialoong | 1 | ✅ | — | 直接合并,squash 时修正标题 "Use" → "use" |
+| @yx9o | 1 | ✅ | — | 直接合并 |
+
+## 七、合并与部署结果 (2026-07-24)
+
+### 已合并 PR(16 个)
+
+按 PR 编号顺序 squash merge 到 `rocketmq-studio` 分支:
+
+| PR | 提交 | 标题 |
+|----|------|------|
+| #492 | `b30881fd` | fix: align login response contract |
+| #494 | `608daa2b` | fix: use runtime DNS resolver for Nginx API proxy |
+| #495 | `bce30ba3` | feat: enhance i18n support with labelKey-based translations |
+| #498 | `87f70c3c` | test: fix frontend baseline assertions |
+| #499 | `e1df6857` | fix: initialize deploy helpers before validation |
+| #500 | `c79129f5` | ci: build frontend Docker image |
+| #501 | `0655effa` | fix: load producer topics from Studio API response |
+| #502 | `9d6f2538` | feat: add LiteTopic backend endpoints |
+| #504 | `4dbdc19a` | feat: add ops backend endpoints |
+| #505 | `e9329a57` | feat: add proxy address endpoints |
+| #506 | `db2420f6` | feat: add LLM settings endpoints |
+| #507 | `fd7001dc` | feat: add alert rules YAML endpoint |
+| #509 | `100be0f4` | fix: generate data source keys on creation |
+| #510 | `c5e31009` | feat: add producer connection endpoint |
+| #512 | `d06c7738` | fix: avoid returning General Settings API key |
+| #514 | `25395a00` | feat: add catalog-driven read-only tools |
+
+### 未合并 PR(3 个)
+
+| PR | 原因 |
+|----|------|
+| #496 | Request Changes:auth API 路径与后端不匹配(`/login/login.do` vs `/api/auth`),合并会打挂登录 |
+| #497 | Request Changes:MiniLine `_lineId` 模块级可变状态应改 useId();CSS 大量 `!important` 维护性差 |
+| #515 | DRAFT:代码质量达标,待移除 DRAFT 标记后合并 |
+
+### 构建验证
+
+| 检查项 | 结果 | 说明 |
+|--------|------|------|
+| 前端编译 (`npm ci && npm run build`) | ✅ | tsc + vite 通过,7721 modules,1.96MB JS |
+| 后端编译 (`mvn package -DskipTests`) | ✅ | BUILD SUCCESS,9.5s |
+
+### 部署验证 (47.83.18.255)
+
+| 检查项 | 结果 | 说明 |
+|--------|------|------|
+| docker compose build | ✅ | rocketmq-server + rocketmq-web 镜像构建成功 |
+| 容器启动 | ✅ | 两个容器均为 Up 状态 |
+| 前端健康检查 (`curl :6789/`) | ✅ HTTP 200 | Nginx 正常服务 |
+| 后端健康检查 (`curl :6789/actuator/health`) | ✅ HTTP 200 | Spring Boot UP |
+| API 代理 (`GET /api/settings/general`) | ✅ | 返回正确 JSON,含 `apiKeyConfigured: false` 字段(#512 生效) |
+| Ops 端点 (`GET /api/ops/homePage`) | ✅ | 返回 namesvrAddrList, useVIPChannel, useTLS(#504 生效) |
+| Proxy 端点 (`GET /api/proxy/homePage.query`) | ✅ | 返回 proxyAddrList, currentProxyAddr(#505 生效) |
+| LLM 端点 (`GET /api/llm/config`) | ✅ | 返回 provider, model, apiBase 等配置(#506 生效,未用 Result<> 包装) |
+
+## 八、第二轮审查与合并 (2026-07-24)
+
+### 新增 PR 审查(6 个)
+
+| PR | 作者 | 中文描述 | 代码 | 测试 | 安全 | API 路径 | 评审结论 |
+|----|------|----------|------|------|------|----------|----------|
+| #516 | @Aias00 | ACL 管理页面从 mock 切换到后端 API,新增 update 端点 | ✅ | ✅ | ✅ | ✅ | ✅ **已合并** |
+| #517 | @Aias00 | 数据源连接测试改为真实探测 Prometheus 端点 | ✅ | ✅ | ⚠️ SSRF | ✅ | **Comment**:SSRF 风险需评估 |
+| #518 | @Aias00 | Consumer Group 页面从 mock 切换到后端 API | ⚠️ | ✅ | ✅ | ✅ | **Request Changes**:删除缺错误处理;i18n 硬编码中文 |
+| #519 | @Aias00 | 消息查询结果按 storeTime 降序排序 | ✅ | ✅ | ✅ | ✅ | ✅ **已合并** |
+| #520 | @Aias00 | Topic 页面动态加载命名空间列表 | ✅ | ✅ | ✅ | ✅ | ❌ **已关闭**(当前设计不需要命名空间) |
+| #521 | @Aias00 | 新增 Consumer 线程堆栈诊断 API | ✅ | ✅ | ✅ | ✅ | ✅ **已合并** |
+
+> **PR 标题规范说明**:PR 标题带 `[Studio]` 前缀,squash merge 时统一去掉,commit message 仅保留 `type: description (#PR)`。这是项目有意设计。
+
+### 第二轮合并 PR(3 个)
+
+| PR | 提交 | squash 标题 |
+|----|------|-------------|
+| #516 | `4240f28c` | fix: ACL page API integration |
+| #519 | `208540b8` | fix: sort queried messages by store time |
+| #521 | `5155a180` | feat: add consumer stack diagnostics API |
+
+### 构建与部署验证
+
+| 检查项 | 结果 |
+|--------|------|
+| 前端编译 (`npm run build`) | ✅ tsc + vite 通过 |
+| 后端编译 (`mvn package -DskipTests`) | ✅ BUILD SUCCESS |
+| docker compose 部署 | ✅ 容器启动成功 |
+| 前端 `:6789/` | ✅ HTTP 200 |
+| 后端 `:6789/actuator/health` | ✅ HTTP 200 |
+
+### 重点问题(未关闭)
+
+1. **#517 SSRF 风险**:数据源测试端点对用户 URL 直接发 HTTP 请求,未过滤私有 IP 段(`169.254.169.254`、`10.x`、`172.16-31.x`、`127.x`)。若 Studio 仅内网部署且仅管理员可访问可降级。
+
+2. **#518 删除操作缺错误处理**:`deleteConsumerGroup` 和 `batchDeleteConsumerGroups` 的 `onOk` 没有 try/catch,API 失败时用户无反馈。错误消息硬编码中文不符合 i18n 规范。
+
+### 当前 open PR 状态汇总
+
+| 类别 | PR 编号 | 数量 |
+|------|---------|------|
+| 已合并(总计) | #492–#514, #516, #519, #521 | 19 |
+| 已关闭 | #520 | 1 |
+| Request Changes | #496, #497, #518 | 3 |
+| Comment | #517 | 1 |
+| DRAFT | #515 | 1 |
diff --git a/docs/progress-20260728.md b/docs/progress-20260728.md
new file mode 100644
index 00000000..b0ffcf38
--- /dev/null
+++ b/docs/progress-20260728.md
@@ -0,0 +1,81 @@
+# RocketMQ Studio 待合并 PR 进展
+
+> 数据来源: `apache/rocketmq-dashboard` GitHub API,筛选 `is:pr is:open`
+> 生成日期: 2026-07-28
+> PR 总数: **44**(已过滤目标分支为 `apache:master` 的 PR)
+
+**过滤规则**: 目标分支为 `apache:master` 的 PR 不属于 Studio 开发线,统计时应排除(如 #503)。
+
+## PR 列表
+
+| # | PR | 作者 | 说明 | 文件数 | +行 | -行 | Review 状态 |
+|---|-----|------|------|--------|-----|-----|-------------|
+| 1 | [#496](https://github.com/apache/rocketmq-dashboard/pull/496) | zhaohai666 | API 层对齐后端接口,补全格式化工具函数 | 8 | +401 | -25 | 🔴 Request Changes |
+| 2 | [#497](https://github.com/apache/rocketmq-dashboard/pull/497) | zhaohai666 | UI 样式优化、MiniLine 图表升级、首页重新设计 | 6 | +686 | -157 | 🔴 Request Changes |
+| 3 | [#525](https://github.com/apache/rocketmq-dashboard/pull/525) | zhaohai666 | 新增核心集群与 Topic 模型 DTO | 10 | +1246 | -0 | — |
+| 4 | [#526](https://github.com/apache/rocketmq-dashboard/pull/526) | zhaohai666 | 新增消息与消息轨迹模型 DTO | 10 | +1082 | -0 | — |
+| 5 | [#527](https://github.com/apache/rocketmq-dashboard/pull/527) | zhaohai666 | 新增消息轨迹图模型 DTO | 5 | +159 | -0 | — |
+| 6 | [#528](https://github.com/apache/rocketmq-dashboard/pull/528) | zhaohai666 | 新增 ACL 与用户模型 DTO | 10 | +1013 | -0 | — |
+| 7 | [#529](https://github.com/apache/rocketmq-dashboard/pull/529) | zhaohai666 | 新增 V5 特性与监控指标模型 DTO | 9 | +999 | -0 | — |
+| 8 | [#530](https://github.com/apache/rocketmq-dashboard/pull/530) | zhaohai666 | 新增请求 DTO 类 | 10 | +654 | -0 | — |
+| 9 | [#531](https://github.com/apache/rocketmq-dashboard/pull/531) | zhaohai666 | 新增用户请求 DTO 与登录模型 | 5 | +163 | -0 | — |
+| 10 | [#532](https://github.com/apache/rocketmq-dashboard/pull/532) | Aias00 | 支持按消息 Tag 查询 | 9 | +96 | -7 | — |
+| 11 | [#533](https://github.com/apache/rocketmq-dashboard/pull/533) | Aias00 | 新增可配置的登录保护机制 | 15 | +496 | -18 | — |
+| 12 | [#536](https://github.com/apache/rocketmq-dashboard/pull/536) | Aias00 | 避免暴露 LLM API Key | 7 | +141 | -27 | — |
+| 13 | [#567](https://github.com/apache/rocketmq-dashboard/pull/567) | yx9o | 校验审计日志日期过滤参数 | 3 | +33 | -12 | — |
+| 14 | [#569](https://github.com/apache/rocketmq-dashboard/pull/569) | yx9o | 保护 ACL 用户凭据安全 | 9 | +339 | -42 | — |
+| 15 | [#575](https://github.com/apache/rocketmq-dashboard/pull/575) | yx9o | 校验告警规则更新请求 | 5 | +132 | -2 | — |
+| 16 | [#577](https://github.com/apache/rocketmq-dashboard/pull/577) | Aias00 | 新增 Proxy 地址移除流程 | 8 | +108 | -6 | — |
+| 17 | [#579](https://github.com/apache/rocketmq-dashboard/pull/579) | yx9o | K8s 证书更新改为原子操作 | 2 | +97 | -17 | — |
+| 18 | [#581](https://github.com/apache/rocketmq-dashboard/pull/581) | yx9o | 拒绝不存在数据源的更新请求 | 5 | +53 | -12 | — |
+| 19 | [#583](https://github.com/apache/rocketmq-dashboard/pull/583) | yx9o | 修正 LiteTopic 会话 POP 进度 | 2 | +90 | -1 | — |
+| 20 | [#585](https://github.com/apache/rocketmq-dashboard/pull/585) | yx9o | 校验 Ops 开关请求 | 4 | +47 | -13 | — |
+| 21 | [#587](https://github.com/apache/rocketmq-dashboard/pull/587) | yx9o | 处理无效请求参数类型 | 2 | +29 | -0 | — |
+| 22 | [#611](https://github.com/apache/rocketmq-dashboard/pull/611) | yx9o | 对 Topic 和消费组路径段做 URL 编码 | 3 | +50 | -7 | — |
+| 23 | [#615](https://github.com/apache/rocketmq-dashboard/pull/615) | yx9o | 校验实例更新请求 | 2 | +52 | -0 | — |
+| 24 | [#620](https://github.com/apache/rocketmq-dashboard/pull/620) | yx9o | 客户端认证时更新 TrustStore 字段 | 2 | +27 | -2 | — |
+| 25 | [#625](https://github.com/apache/rocketmq-dashboard/pull/625) | yx9o | 修正导航路由匹配,保留编辑快捷键 | 2 | +45 | -2 | — |
+| 26 | [#627](https://github.com/apache/rocketmq-dashboard/pull/627) | yx9o | 状态徽章无障碍名称本地化 | 2 | +15 | -2 | — |
+| 27 | [#629](https://github.com/apache/rocketmq-dashboard/pull/629) | yx9o | 零吞吐量时不渲染可见柱状条 | 2 | +38 | -1 | — |
+| 28 | [#630](https://github.com/apache/rocketmq-dashboard/pull/630) | keranbingaa | 集群页面新增 NameServer 增删改查及 localStorage 持久化 | 3 | +106 | -15 | — |
+| 29 | [#631](https://github.com/apache/rocketmq-dashboard/pull/631) | Aias00 | 加固 Mock 服务数据处理 | 18 | +836 | -61 | — |
+| 30 | [#632](https://github.com/apache/rocketmq-dashboard/pull/632) | Aias00 | 加固请求校验处理 | 20 | +771 | -32 | — |
+| 31 | [#633](https://github.com/apache/rocketmq-dashboard/pull/633) | Aias00 | 校验集群操作请求 | 13 | +335 | -7 | — |
+| 32 | [#634](https://github.com/apache/rocketmq-dashboard/pull/634) | Aias00 | 校验实例资源操作请求 | 17 | +448 | -19 | — |
+| 33 | [#635](https://github.com/apache/rocketmq-dashboard/pull/635) | Aias00 | 规范化服务层搜索过滤 | 14 | +301 | -20 | — |
+| 34 | [#636](https://github.com/apache/rocketmq-dashboard/pull/636) | Aias00 | 校验 ACL 请求输入 | 5 | +140 | -5 | — |
+| 35 | [#637](https://github.com/apache/rocketmq-dashboard/pull/637) | Aias00 | 校验监控指标与 Prometheus 查询 | 12 | +689 | -2 | — |
+| 36 | [#638](https://github.com/apache/rocketmq-dashboard/pull/638) | Aias00 | 加固页面数据加载与状态管理 | 14 | +814 | -79 | — |
+| 37 | [#639](https://github.com/apache/rocketmq-dashboard/pull/639) | Aias00 | API 路径段 URL 编码 | 4 | +44 | -3 | — |
+| 38 | [#640](https://github.com/apache/rocketmq-dashboard/pull/640) | Aias00 | 补全 DLQ 数据与操作 | 4 | +333 | -5 | — |
+| 39 | [#641](https://github.com/apache/rocketmq-dashboard/pull/641) | Aias00 | 新增运行时详情弹窗 | 6 | +401 | -4 | — |
+| 40 | [#642](https://github.com/apache/rocketmq-dashboard/pull/642) | Aias00 | 新增 AI 资源工具 | 8 | +754 | -10 | — |
+| 41 | [#643](https://github.com/apache/rocketmq-dashboard/pull/643) | Aias00 | 加固数据源管理 | 9 | +677 | -33 | — |
+| 42 | [#644](https://github.com/apache/rocketmq-dashboard/pull/644) | Aias00 | 加固 K8s 证书管理 | 8 | +207 | -7 | — |
+| 43 | [#645](https://github.com/apache/rocketmq-dashboard/pull/645) | Aias00 | 加固集群仓库与配置状态 | 8 | +215 | -13 | — |
+| 44 | [#651](https://github.com/apache/rocketmq-dashboard/pull/651) | Aias00 | 新增真实 LLM 网关 | 31 | +3607 | -267 | — |
+
+## 统计
+
+| 作者 | PR 数 | 总文件数 | 总 +行 | 总 -行 |
+|------|-------|---------|--------|--------|
+| Aias00 | 20 | 224 | +11,571 | -597 |
+| yx9o | 15 | 45 | +1,095 | -113 |
+| zhaohai666 | 8 | 63 | +6,003 | -182 |
+| keranbingaa | 1 | 3 | +106 | -15 |
+| **合计** | **44** | **335** | **+18,775** | **-907** |
+
+## 本地合并记录(#526–#531)
+
+2026-07-28 在本地 `rocketmq-studio` 分支按顺序合并了 #526–#531(跳过 #525),并统一去除 Lombok 依赖(项目不引入 Lombok)。
+
+| PR | 合并结果 | 修改内容 | 涉及文件 |
+|----|---------|---------|----------|
+| #526 | ✅ 合并成功 | `Entry.java`: 去除 `@Getter/@Setter`,手写 getter/setter | 1 文件 |
+| #527 | ✅ 合并成功 | 5 个 trace 模型类全部去除 `@Data/@Getter`,手写 getter/setter | 5 文件 |
+| #528 | ✅ 合并成功 | `ACLPolicy`、`ACLUser`、`Acl2PolicyContext`(含内部类)、`Policy`、`PolicyRequest`、`UserInfoDto`: 去除 `@Data/@Getter/@Setter/@NoArgsConstructor/@AllArgsConstructor`,手写 getter/setter 和构造器 | 6 文件 |
+| #529 | ✅ 合并成功 | `DlqMessageExcelModel`、`DlqMessageResendResult`、`LiteTopicQuota`、`LiteTopicSession`(含内部类)、`LiteTopicSummary`、`MetricsDataSourceConfig`、`MetricsHealthResult`: 去除 `@Data/@NoArgsConstructor`,手写 getter/setter 和构造器 | 7 文件 |
+| #530 | ✅ 合并成功 | `AclRequest`、`MetricsDataSourceRequest`、`SendTopicMessageRequest`: 去除 `@Data/@Getter/@Setter`,手写 getter/setter | 3 文件 |
+| #531 | ✅ 合并成功 | `LoginResult`、`UserCreateRequest`、`UserInfoParam`、`UserUpdateRequest`: 去除 `@Data/@Getter/@Setter/@AllArgsConstructor/@NoArgsConstructor`,手写 getter/setter 和构造器 | 4 文件 |
+
+**修改原则**: 项目不引入 Lombok,所有 `@Data`、`@Getter`、`@Setter`、`@NoArgsConstructor`、`@AllArgsConstructor` 注解均替换为等价的手写代码。业务逻辑方法(如 `isEffective()`、`validate()`、`getUsageRate()` 等)保持不变。
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ACLPolicy.java b/server/src/main/java/org/apache/rocketmq/studio/model/ACLPolicy.java
new file mode 100644
index 00000000..808d5dd9
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ACLPolicy.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Set;
+
+public class ACLPolicy {
+
+    private String policyId;
+
+    private String policyName;
+
+    private String description;
+
+    private Set<String> users;
+
+    private Set<String> resources;
+
+    private Set<String> actions;
+
+    private String policyType;
+
+    private Set<String> ipWhiteList;
+
+    private Date effectiveTime;
+
+    private Date expirationTime;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String status;
+
+    private Boolean defaultPolicy;
+
+    public boolean isEffective() {
+        Date now = new Date();
+        if (effectiveTime != null && now.before(effectiveTime)) {
+            return false;
+        }
+        if (expirationTime != null && now.after(expirationTime)) {
+            return false;
+        }
+        if (!"ACTIVE".equals(status)) {
+            return false;
+        }
+        return true;
+    }
+
+    public boolean hasPermission(String user, String resource, String action) {
+        if (!isEffective()) {
+            return false;
+        }
+
+        if (users != null && !users.isEmpty() && !users.contains(user)) {
+            return false;
+        }
+
+        if (resources != null && !resources.isEmpty() && !resources.contains(resource)) {
+            return false;
+        }
+
+        if (actions != null && !actions.contains(action)) {
+            return false;
+        }
+
+        return "ALLOW".equals(policyType);
+    }
+    public String getPolicyId() {
+        return policyId;
+    }
+
+    public void setPolicyId(String policyId) {
+        this.policyId = policyId;
+    }
+
+    public String getPolicyName() {
+        return policyName;
+    }
+
+    public void setPolicyName(String policyName) {
+        this.policyName = policyName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Set<String> getUsers() {
+        return users;
+    }
+
+    public void setUsers(Set<String> users) {
+        this.users = users;
+    }
+
+    public Set<String> getResources() {
+        return resources;
+    }
+
+    public void setResources(Set<String> resources) {
+        this.resources = resources;
+    }
+
+    public Set<String> getActions() {
+        return actions;
+    }
+
+    public void setActions(Set<String> actions) {
+        this.actions = actions;
+    }
+
+    public String getPolicyType() {
+        return policyType;
+    }
+
+    public void setPolicyType(String policyType) {
+        this.policyType = policyType;
+    }
+
+    public Set<String> getIpWhiteList() {
+        return ipWhiteList;
+    }
+
+    public void setIpWhiteList(Set<String> ipWhiteList) {
+        this.ipWhiteList = ipWhiteList;
+    }
+
+    public Date getEffectiveTime() {
+        return effectiveTime;
+    }
+
+    public void setEffectiveTime(Date effectiveTime) {
+        this.effectiveTime = effectiveTime;
+    }
+
+    public Date getExpirationTime() {
+        return expirationTime;
+    }
+
+    public void setExpirationTime(Date expirationTime) {
+        this.expirationTime = expirationTime;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Boolean getDefaultPolicy() {
+        return defaultPolicy;
+    }
+
+    public void setDefaultPolicy(Boolean defaultPolicy) {
+        this.defaultPolicy = defaultPolicy;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ACLUser.java b/server/src/main/java/org/apache/rocketmq/studio/model/ACLUser.java
new file mode 100644
index 00000000..e7d850c5
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ACLUser.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed according to the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Set;
+
+public class ACLUser {
+
+    private String userName;
+
+    /**
+     * AccessKey
+     */
+    private String accessKey;
+
+    private String userType;
+
+    private String status;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private Date lastLoginTime;
+
+    private Set<String> policyIds;
+
+    private Set<String> ipWhiteList;
+
+    private String description;
+
+    public boolean isActive() {
+        return "ACTIVE".equals(status);
+    }
+
+    public boolean isAdmin() {
+        return "ADMIN".equals(userType);
+    }
+
+    public boolean isIpAllowed(String ip) {
+        if (ipWhiteList == null || ipWhiteList.isEmpty()) {
+            return true;
+        }
+        return ipWhiteList.contains(ip);
+    }
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getUserType() {
+        return userType;
+    }
+
+    public void setUserType(String userType) {
+        this.userType = userType;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Date getLastLoginTime() {
+        return lastLoginTime;
+    }
+
+    public void setLastLoginTime(Date lastLoginTime) {
+        this.lastLoginTime = lastLoginTime;
+    }
+
+    public Set<String> getPolicyIds() {
+        return policyIds;
+    }
+
+    public void setPolicyIds(Set<String> policyIds) {
+        this.policyIds = policyIds;
+    }
+
+    public Set<String> getIpWhiteList() {
+        return ipWhiteList;
+    }
+
+    public void setIpWhiteList(Set<String> ipWhiteList) {
+        this.ipWhiteList = ipWhiteList;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/AccessControlList.java b/server/src/main/java/org/apache/rocketmq/studio/model/AccessControlList.java
new file mode 100644
index 00000000..9e85e9cf
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/AccessControlList.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.List;
+
+/**
+ * Access Control List model for ACL management.
+ * Represents the ACL configuration for a broker.
+ */
+public class AccessControlList {
+
+    private String brokerAddr;
+    private String clusterName;
+    private List<AccessControlEntry> entries;
+    private long version;
+
+    public String getBrokerAddr() {
+        return brokerAddr;
+    }
+
+    public void setBrokerAddr(String brokerAddr) {
+        this.brokerAddr = brokerAddr;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public List<AccessControlEntry> getEntries() {
+        return entries;
+    }
+
+    public void setEntries(List<AccessControlEntry> entries) {
+        this.entries = entries;
+    }
+
+    public long getVersion() {
+        return version;
+    }
+
+    public void setVersion(long version) {
+        this.version = version;
+    }
+
+    /**
+     * Individual access control entry within an ACL.
+     */
+    public static class AccessControlEntry {
+
+        private String accessKey;
+        private String secretKey;
+        private String admin;
+        private String defaultTopicPerm;
+        private String defaultGroupPerm;
+        private List<String> topicPerms;
+        private List<String> groupPerms;
+
+        public String getAccessKey() {
+            return accessKey;
+        }
+
+        public void setAccessKey(String accessKey) {
+            this.accessKey = accessKey;
+        }
+
+        public String getSecretKey() {
+            return secretKey;
+        }
+
+        public void setSecretKey(String secretKey) {
+            this.secretKey = secretKey;
+        }
+
+        public String getAdmin() {
+            return admin;
+        }
+
+        public void setAdmin(String admin) {
+            this.admin = admin;
+        }
+
+        public String getDefaultTopicPerm() {
+            return defaultTopicPerm;
+        }
+
+        public void setDefaultTopicPerm(String defaultTopicPerm) {
+            this.defaultTopicPerm = defaultTopicPerm;
+        }
+
+        public String getDefaultGroupPerm() {
+            return defaultGroupPerm;
+        }
+
+        public void setDefaultGroupPerm(String defaultGroupPerm) {
+            this.defaultGroupPerm = defaultGroupPerm;
+        }
+
+        public List<String> getTopicPerms() {
+            return topicPerms;
+        }
+
+        public void setTopicPerms(List<String> topicPerms) {
+            this.topicPerms = topicPerms;
+        }
+
+        public List<String> getGroupPerms() {
+            return groupPerms;
+        }
+
+        public void setGroupPerms(List<String> groupPerms) {
+            this.groupPerms = groupPerms;
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/Acl2PolicyContext.java b/server/src/main/java/org/apache/rocketmq/studio/model/Acl2PolicyContext.java
new file mode 100644
index 00000000..268b61af
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/Acl2PolicyContext.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * ACL 2.0 Policy Context - DTO for passing enhanced ACL 2.0 policy data between Controller and Service layers.
+ *
+ * This model extends ACL 1.0 compatible fields with ACL 2.0 RBAC-specific extensions including:
+ * - Policy name as unique identifier
+ * - Binding type (USER / GROUP / SERVICE_ACCOUNT)
+ * - Fine-grained authorization rules with resource patterns and effects
+ *
+ * @see ACLPolicy
+ */
+public class Acl2PolicyContext {
+
+    // ========== Base Fields (Compatible with ACL 1.0) ==========
+
+    /** Access key (user identifier) */
+    private String accessKey;
+
+    /** Secret key for authentication */
+    private String secretKey;
+
+    /** Whether this is an admin user */
+    private boolean isAdmin;
+
+    /** IP whitelist patterns */
+    private List<String> whiteSet;
+
+    // ========== ACL 2.0 RBAC Extended Fields ==========
+
+    /** Policy name (unique identifier for ACL 2.0 policies) */
+    private String policyName;
+
+    /** Binding type: USER / GROUP / SERVICE_ACCOUNT */
+    private String boundType;
+
+    /** Bound entity ID (e.g., username, group name) */
+    private String boundEntityId;
+
+    /** List of authorization rules */
+    private List<AuthorizationRule> rules;
+
+    /** Whether this policy is enabled */
+    private boolean enabled;
+
+    /** Policy description */
+    private String description;
+
+    /** Cluster scope for this policy */
+    private String clusterName;
+
+    /** Broker scope for this policy */
+    private String brokerName;
+
+    /** Metadata for tracking creation/update */
+    private Date createTime;
+
+    /** Last update time */
+    private Date updateTime;
+
+    /**
+     * Authorization Rule - defines what actions are allowed/denied on which resources.
+     * Supports wildcards in resource patterns following path pattern syntax.
+     */
+    public static class AuthorizationRule {
+
+        /** Resource path pattern supporting wildcards (* matches any single level, ** matches all levels) */
+        private String resourcePattern;
+
+        /** Allowed operations: READ / WRITE / ADMIN / DELETE / UPDATE / CREATE / SUBSCRIBE / CONSUME */
+        private List<String> actions;
+
+        /** Effect: Allow or Deny (default is Allow when not specified) */
+        private String effect;
+
+        /** Priority: lower values are matched first. Default is 100. */
+        private int priority;
+
+        /** Optional rule description */
+        private String description;
+
+        /** Create a default Allow rule for backward compatibility */
+        public static AuthorizationRule defaultAllowRule(String resourcePattern) {
+            AuthorizationRule rule = new AuthorizationRule();
+            rule.setResourcePattern(resourcePattern);
+            rule.setActions(List.of("READ", "WRITE"));
+            rule.setEffect("Allow");
+            rule.setPriority(100);
+            return rule;
+        }
+
+        /** Create a deny-all rule */
+        public static AuthorizationRule denyAllRule() {
+            AuthorizationRule rule = new AuthorizationRule();
+            rule.setResourcePattern("**");
+            rule.setActions(List.of("*"));
+            rule.setEffect("Deny");
+            rule.setPriority(0);
+            return rule;
+        }
+
+        public String getResourcePattern() {
+            return resourcePattern;
+        }
+
+        public void setResourcePattern(String resourcePattern) {
+            this.resourcePattern = resourcePattern;
+        }
+
+        public List<String> getActions() {
+            return actions;
+        }
+
+        public void setActions(List<String> actions) {
+            this.actions = actions;
+        }
+
+        public String getEffect() {
+            return effect;
+        }
+
+        public void setEffect(String effect) {
+            this.effect = effect;
+        }
+
+        public int getPriority() {
+            return priority;
+        }
+
+        public void setPriority(int priority) {
+            this.priority = priority;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+    }
+
+    /**
+     * Validate this policy context for consistency.
+     *
+     * @throws IllegalArgumentException if required fields are missing
+     */
+    public void validate() {
+        if (accessKey == null || accessKey.trim().isEmpty()) {
+            throw new IllegalArgumentException("accessKey cannot be empty");
+        }
+        if (policyName == null || policyName.trim().isEmpty()) {
+            throw new IllegalArgumentException("policyName cannot be empty");
+        }
+        if (rules != null && rules.isEmpty()) {
+            throw new IllegalArgumentException("rules list must not be empty");
+        }
+        if (rules != null) {
+            for (int i = 0; i < rules.size(); i++) {
+                AuthorizationRule rule = rules.get(i);
+                if (rule.getResourcePattern() == null || rule.getResourcePattern().trim().isEmpty()) {
+                    throw new IllegalArgumentException(
+                        String.format("rules[%d].resourcePattern cannot be empty", i));
+                }
+                if (rule.getActions() == null || rule.getActions().isEmpty()) {
+                    throw new IllegalArgumentException(
+                        String.format("rules[%d].actions cannot be empty", i));
+                }
+                String effect = rule.getEffect();
+                if (effect != null && !"Allow".equalsIgnoreCase(effect) && !"Deny".equalsIgnoreCase(effect)) {
+                    throw new IllegalArgumentException(
+                        String.format("rules[%d].effect must be 'Allow' or 'Deny', got: %s", i, effect));
+                }
+                if (effect == null) {
+                    rule.setEffect("Allow");
+                }
+            }
+        }
+        if (boundType != null && !isValidBoundType(boundType)) {
+            throw new IllegalArgumentException(
+                String.format("boundType must be USER, GROUP, or SERVICE_ACCOUNT, got: %s", boundType));
+        }
+    }
+
+    private boolean isValidBoundType(String type) {
+        return "USER".equals(type) || "GROUP".equals(type) || "SERVICE_ACCOUNT".equals(type);
+    }
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    public boolean isIsAdmin() {
+        return isAdmin;
+    }
+
+    public void setIsAdmin(boolean isAdmin) {
+        this.isAdmin = isAdmin;
+    }
+
+    public List<String> getWhiteSet() {
+        return whiteSet;
+    }
+
+    public void setWhiteSet(List<String> whiteSet) {
+        this.whiteSet = whiteSet;
+    }
+
+    public String getPolicyName() {
+        return policyName;
+    }
+
+    public void setPolicyName(String policyName) {
+        this.policyName = policyName;
+    }
+
+    public String getBoundType() {
+        return boundType;
+    }
+
+    public void setBoundType(String boundType) {
+        this.boundType = boundType;
+    }
+
+    public String getBoundEntityId() {
+        return boundEntityId;
+    }
+
+    public void setBoundEntityId(String boundEntityId) {
+        this.boundEntityId = boundEntityId;
+    }
+
+    public List<AuthorizationRule> getRules() {
+        return rules;
+    }
+
+    public void setRules(List<AuthorizationRule> rules) {
+        this.rules = rules;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ClientInstance.java b/server/src/main/java/org/apache/rocketmq/studio/model/ClientInstance.java
new file mode 100644
index 00000000..e608ee81
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ClientInstance.java
@@ -0,0 +1,373 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.List;
+
+public class ClientInstance {
+
+    private String clientId;
+
+    private String clientAddress;
+
+    private ClientType clientType;
+
+    /**
+     * Topics associated with this client (published topics for producers, subscribed topics for consumers)
+     */
+    private List<String> topics;
+
+    private String clientSubType;
+
+    private String language;
+
+    private String sdkVersion;
+
+    private ProtocolType protocolType;
+
+    private String endpoint;
+
+    private Date lastHeartbeatTime;
+
+    private boolean active;
+
+    private Date connectTime;
+
+    private String instanceName;
+
+    private String consumerGroup;
+
+    private String producerGroup;
+
+    private List<SubscriptionInfo> subscriptions;
+
+    private List<String> publishTopics;
+
+    private ConsumerProgress consumerProgress;
+
+    private String clientVersion;
+
+    private Boolean vipChannelEnabled;
+
+    private String telemetrySessionId;
+
+    private Boolean longConnectionActive;
+
+    private String settingsVersion;
+
+    private String authFailureReason;
+
+    private Boolean popEnabled;
+
+    private Integer pendingAckCount;
+
+    /**
+     * Subscription count for consumer clients
+     */
+    private Integer subscriptionCount;
+
+    /**
+     * Client status (ONLINE, OFFLINE, etc.)
+     */
+    private String status;
+
+    public String getDisplayName() {
+        if (instanceName != null && !instanceName.trim().isEmpty()) {
+            return instanceName;
+        }
+        return clientId;
+    }
+
+    public long getClientDelay() {
+        if (lastHeartbeatTime == null) {
+            return -1;
+        }
+        return (System.currentTimeMillis() - lastHeartbeatTime.getTime()) / 1000;
+    }
+
+    public boolean isGrpcClient() {
+        return ProtocolType.GRPC.equals(protocolType);
+    }
+
+    public boolean isRemotingClient() {
+        return ProtocolType.REMOTING.equals(protocolType);
+    }
+
+    public boolean isConsumer() {
+        return ClientType.CONSUMER.equals(clientType);
+    }
+
+    public boolean isProducer() {
+        return ClientType.PRODUCER.equals(clientType);
+    }
+
+    /**
+     * Get version string (alias for sdkVersion for backward compatibility)
+     */
+    public String getVersion() {
+        return sdkVersion;
+    }
+
+    /**
+     * Set version string (alias for sdkVersion for backward compatibility)
+     */
+    public void setVersion(String version) {
+        this.sdkVersion = version;
+    }
+
+    public enum ClientType {
+        PRODUCER, CONSUMER, PUSH_CONSUMER, PULL_CONSUMER, SIMPLE_CONSUMER
+    }
+
+    public enum ProtocolType {
+        REMOTING, GRPC
+    }
+
+    public static class ConsumerProgress {
+        private Long totalConsumed;
+        private Long totalBacklog;
+        private Double consumptionRate;
+        private Date lastConsumeTime;
+        private String consumptionMode; // PULL / PUSH / POP
+        private Boolean orderlyConsume;
+    }
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getClientAddress() {
+        return clientAddress;
+    }
+
+    public void setClientAddress(String clientAddress) {
+        this.clientAddress = clientAddress;
+    }
+
+    public ClientType getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientType clientType) {
+        this.clientType = clientType;
+    }
+
+    public List<String> getTopics() {
+        return topics;
+    }
+
+    public void setTopics(List<String> topics) {
+        this.topics = topics;
+    }
+
+    public String getClientSubType() {
+        return clientSubType;
+    }
+
+    public void setClientSubType(String clientSubType) {
+        this.clientSubType = clientSubType;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public String getSdkVersion() {
+        return sdkVersion;
+    }
+
+    public void setSdkVersion(String sdkVersion) {
+        this.sdkVersion = sdkVersion;
+    }
+
+    public ProtocolType getProtocolType() {
+        return protocolType;
+    }
+
+    public void setProtocolType(ProtocolType protocolType) {
+        this.protocolType = protocolType;
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public Date getLastHeartbeatTime() {
+        return lastHeartbeatTime;
+    }
+
+    public void setLastHeartbeatTime(Date lastHeartbeatTime) {
+        this.lastHeartbeatTime = lastHeartbeatTime;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+    public Date getConnectTime() {
+        return connectTime;
+    }
+
+    public void setConnectTime(Date connectTime) {
+        this.connectTime = connectTime;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    public void setInstanceName(String instanceName) {
+        this.instanceName = instanceName;
+    }
+
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
+    public String getProducerGroup() {
+        return producerGroup;
+    }
+
+    public void setProducerGroup(String producerGroup) {
+        this.producerGroup = producerGroup;
+    }
+
+    public List<SubscriptionInfo> getSubscriptions() {
+        return subscriptions;
+    }
+
+    public void setSubscriptions(List<SubscriptionInfo> subscriptions) {
+        this.subscriptions = subscriptions;
+    }
+
+    public List<String> getPublishTopics() {
+        return publishTopics;
+    }
+
+    public void setPublishTopics(List<String> publishTopics) {
+        this.publishTopics = publishTopics;
+    }
+
+    public ConsumerProgress getConsumerProgress() {
+        return consumerProgress;
+    }
+
+    public void setConsumerProgress(ConsumerProgress consumerProgress) {
+        this.consumerProgress = consumerProgress;
+    }
+
+    public String getClientVersion() {
+        return clientVersion;
+    }
+
+    public void setClientVersion(String clientVersion) {
+        this.clientVersion = clientVersion;
+    }
+
+    public Boolean getVipChannelEnabled() {
+        return vipChannelEnabled;
+    }
+
+    public void setVipChannelEnabled(Boolean vipChannelEnabled) {
+        this.vipChannelEnabled = vipChannelEnabled;
+    }
+
+    public String getTelemetrySessionId() {
+        return telemetrySessionId;
+    }
+
+    public void setTelemetrySessionId(String telemetrySessionId) {
+        this.telemetrySessionId = telemetrySessionId;
+    }
+
+    public Boolean getLongConnectionActive() {
+        return longConnectionActive;
+    }
+
+    public void setLongConnectionActive(Boolean longConnectionActive) {
+        this.longConnectionActive = longConnectionActive;
+    }
+
+    public String getSettingsVersion() {
+        return settingsVersion;
+    }
+
+    public void setSettingsVersion(String settingsVersion) {
+        this.settingsVersion = settingsVersion;
+    }
+
+    public String getAuthFailureReason() {
+        return authFailureReason;
+    }
+
+    public void setAuthFailureReason(String authFailureReason) {
+        this.authFailureReason = authFailureReason;
+    }
+
+    public Boolean getPopEnabled() {
+        return popEnabled;
+    }
+
+    public void setPopEnabled(Boolean popEnabled) {
+        this.popEnabled = popEnabled;
+    }
+
+    public Integer getPendingAckCount() {
+        return pendingAckCount;
+    }
+
+    public void setPendingAckCount(Integer pendingAckCount) {
+        this.pendingAckCount = pendingAckCount;
+    }
+
+    public Integer getSubscriptionCount() {
+        return subscriptionCount;
+    }
+
+    public void setSubscriptionCount(Integer subscriptionCount) {
+        this.subscriptionCount = subscriptionCount;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ClusterCapability.java b/server/src/main/java/org/apache/rocketmq/studio/model/ClusterCapability.java
new file mode 100644
index 00000000..750dd85c
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ClusterCapability.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ClusterCapability {
+
+    private boolean liteTopicSupported;
+
+    private boolean popConsumeSupported;
+
+    private boolean aclV2Supported;
+
+    private boolean grpcClientSupported;
+
+    private boolean delayMessageSupported;
+
+    private boolean transactionMessageSupported;
+
+    private boolean fifoMessageSupported;
+
+    private String architectureVersion;
+
+    private String rocketmqVersion;
+
+    private Set<String> extendedCapabilities;
+
+    public Set<TopicType> getSupportedTopicTypes() {
+        Set<TopicType> supported = new HashSet<>();
+        supported.add(TopicType.NORMAL);
+
+        if (fifoMessageSupported) {
+            supported.add(TopicType.FIFO);
+        }
+        if (delayMessageSupported) {
+            supported.add(TopicType.DELAY);
+        }
+        if (transactionMessageSupported) {
+            supported.add(TopicType.TRANSACTION);
+        }
+        if (liteTopicSupported) {
+            supported.add(TopicType.LITE);
+        }
+
+        return supported;
+    }
+
+    public boolean hasCapability(String capability) {
+        if (extendedCapabilities != null) {
+            return extendedCapabilities.contains(capability);
+        }
+        return false;
+    }
+    public boolean isLiteTopicSupported() {
+        return liteTopicSupported;
+    }
+
+    public void setLiteTopicSupported(boolean liteTopicSupported) {
+        this.liteTopicSupported = liteTopicSupported;
+    }
+
+    public boolean isPopConsumeSupported() {
+        return popConsumeSupported;
+    }
+
+    public void setPopConsumeSupported(boolean popConsumeSupported) {
+        this.popConsumeSupported = popConsumeSupported;
+    }
+
+    public boolean isAclV2Supported() {
+        return aclV2Supported;
+    }
+
+    public void setAclV2Supported(boolean aclV2Supported) {
+        this.aclV2Supported = aclV2Supported;
+    }
+
+    public boolean isGrpcClientSupported() {
+        return grpcClientSupported;
+    }
+
+    public void setGrpcClientSupported(boolean grpcClientSupported) {
+        this.grpcClientSupported = grpcClientSupported;
+    }
+
+    public boolean isDelayMessageSupported() {
+        return delayMessageSupported;
+    }
+
+    public void setDelayMessageSupported(boolean delayMessageSupported) {
+        this.delayMessageSupported = delayMessageSupported;
+    }
+
+    public boolean isTransactionMessageSupported() {
+        return transactionMessageSupported;
+    }
+
+    public void setTransactionMessageSupported(boolean transactionMessageSupported) {
+        this.transactionMessageSupported = transactionMessageSupported;
+    }
+
+    public boolean isFifoMessageSupported() {
+        return fifoMessageSupported;
+    }
+
+    public void setFifoMessageSupported(boolean fifoMessageSupported) {
+        this.fifoMessageSupported = fifoMessageSupported;
+    }
+
+    public String getArchitectureVersion() {
+        return architectureVersion;
+    }
+
+    public void setArchitectureVersion(String architectureVersion) {
+        this.architectureVersion = architectureVersion;
+    }
+
+    public String getRocketmqVersion() {
+        return rocketmqVersion;
+    }
+
+    public void setRocketmqVersion(String rocketmqVersion) {
+        this.rocketmqVersion = rocketmqVersion;
+    }
+
+    public Set<String> getExtendedCapabilities() {
+        return extendedCapabilities;
+    }
+
+    public void setExtendedCapabilities(Set<String> extendedCapabilities) {
+        this.extendedCapabilities = extendedCapabilities;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ClusterTopology.java b/server/src/main/java/org/apache/rocketmq/studio/model/ClusterTopology.java
new file mode 100644
index 00000000..d6474409
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ClusterTopology.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unified cluster topology model
+ * Hide differences between different cluster architectures
+ */
+public class ClusterTopology {
+
+    /**
+     * Cluster name
+     */
+    private String clusterName;
+
+    /**
+     * NameServer address list
+     */
+    private List<String> namesrvAddresses;
+
+    /**
+     * NameServer node list
+     */
+    private List<NodeInfo> namesrvNodes;
+
+    /**
+     * Broker node list
+     */
+    private List<NodeInfo> brokerNodes;
+
+    /**
+     * Proxy node list (5.0 architecture)
+     */
+    private List<NodeInfo> proxyNodes;
+
+    /**
+     * Node mapping table (for quick lookup)
+     */
+    private Map<String, NodeInfo> nodeMap;
+
+    public ClusterTopology() {
+        this.namesrvAddresses = new ArrayList<>();
+        this.namesrvNodes = new ArrayList<>();
+        this.brokerNodes = new ArrayList<>();
+        this.proxyNodes = new ArrayList<>();
+        this.nodeMap = new HashMap<>();
+    }
+
+    /**
+     * Add node
+     */
+    public void addNode(String nodeName, Long nodeId, String nodeAddress, String nodeType) {
+        NodeInfo node = new NodeInfo();
+        node.setNodeName(nodeName);
+        node.setNodeId(nodeId);
+        node.setNodeAddress(nodeAddress);
+        node.setNodeType(nodeType);
+        node.setClusterName(clusterName);
+
+        String key = nodeType + "-" + nodeName + "-" + nodeId;
+        nodeMap.put(key, node);
+
+        switch (nodeType) {
+            case "NAMESRV":
+                namesrvNodes.add(node);
+                break;
+            case "BROKER":
+                brokerNodes.add(node);
+                break;
+            case "PROXY":
+                proxyNodes.add(node);
+                break;
+        }
+    }
+
+    /**
+     * Get total node count
+     */
+    public int getTotalNodeCount() {
+        return namesrvNodes.size() + brokerNodes.size() + proxyNodes.size();
+    }
+
+    /**
+     * Get master broker count
+     */
+    public int getMasterBrokerCount() {
+        return (int) brokerNodes.stream()
+            .filter(node -> node.getNodeId() != null && node.getNodeId() == 0)
+            .count();
+    }
+
+    /**
+     * Get slave broker count
+     */
+    public int getSlaveBrokerCount() {
+        return (int) brokerNodes.stream()
+            .filter(node -> node.getNodeId() != null && node.getNodeId() > 0)
+            .count();
+    }
+
+
+    public static class NodeInfo {
+        private String nodeName;
+        private Long nodeId;
+        private String nodeAddress;
+        private String nodeType;
+        private String clusterName;
+        private String status;
+        private Long version;
+        private Map<String, Object> metadata;
+
+        public NodeInfo() {
+            this.metadata = new HashMap<>();
+            this.status = "UNKNOWN";
+        }
+
+        public boolean isMaster() {
+            return nodeId != null && nodeId == 0;
+        }
+
+        public boolean isOnline() {
+            return "ONLINE".equals(status);
+        }
+
+        public String getNodeName() {
+            return nodeName;
+        }
+
+        public void setNodeName(String nodeName) {
+            this.nodeName = nodeName;
+        }
+
+        public Long getNodeId() {
+            return nodeId;
+        }
+
+        public void setNodeId(Long nodeId) {
+            this.nodeId = nodeId;
+        }
+
+        public String getNodeAddress() {
+            return nodeAddress;
+        }
+
+        public void setNodeAddress(String nodeAddress) {
+            this.nodeAddress = nodeAddress;
+        }
+
+        public String getNodeType() {
+            return nodeType;
+        }
+
+        public void setNodeType(String nodeType) {
+            this.nodeType = nodeType;
+        }
+
+        public String getClusterName() {
+            return clusterName;
+        }
+
+        public void setClusterName(String clusterName) {
+            this.clusterName = clusterName;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        public void setStatus(String status) {
+            this.status = status;
+        }
+
+        public Long getVersion() {
+            return version;
+        }
+
+        public void setVersion(Long version) {
+            this.version = version;
+        }
+
+        public Map<String, Object> getMetadata() {
+            return metadata;
+        }
+
+        public void setMetadata(Map<String, Object> metadata) {
+            this.metadata = metadata;
+        }
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public List<String> getNamesrvAddresses() {
+        return namesrvAddresses;
+    }
+
+    public void setNamesrvAddresses(List<String> namesrvAddresses) {
+        this.namesrvAddresses = namesrvAddresses;
+    }
+
+    public List<NodeInfo> getNamesrvNodes() {
+        return namesrvNodes;
+    }
+
+    public void setNamesrvNodes(List<NodeInfo> namesrvNodes) {
+        this.namesrvNodes = namesrvNodes;
+    }
+
+    public List<NodeInfo> getBrokerNodes() {
+        return brokerNodes;
+    }
+
+    public void setBrokerNodes(List<NodeInfo> brokerNodes) {
+        this.brokerNodes = brokerNodes;
+    }
+
+    public List<NodeInfo> getProxyNodes() {
+        return proxyNodes;
+    }
+
+    public void setProxyNodes(List<NodeInfo> proxyNodes) {
+        this.proxyNodes = proxyNodes;
+    }
+
+    public Map<String, NodeInfo> getNodeMap() {
+        return nodeMap;
+    }
+
+    public void setNodeMap(Map<String, NodeInfo> nodeMap) {
+        this.nodeMap = nodeMap;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerGroupInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerGroupInfo.java
new file mode 100644
index 00000000..db0b7500
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerGroupInfo.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+public class ConsumerGroupInfo {
+
+    private String consumerGroupName;
+
+    private String consumeMode;
+
+    private Boolean consumeMessageOrderly;
+
+    private Boolean consumeBroadcastEnable;
+
+    private Boolean consumeFromMinEnable;
+
+    private Integer retryQueueNums;
+
+    private Integer retryMaxTimes;
+
+    private Integer consumeTimeoutMinute;
+
+    private String clusterName;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String status;
+
+    private Set<String> subscribedTopics;
+
+    private Integer onlineClientCount;
+
+    private String liteBindTopic;
+
+    private Map<String, String> attributes;
+
+    private Integer groupSysFlag;
+
+    public String getDisplayName() {
+        return consumerGroupName;
+    }
+
+    public boolean isPopConsumer() {
+        return "POP".equalsIgnoreCase(consumeMode);
+    }
+
+    public boolean isOrderlyConsume() {
+        return Boolean.TRUE.equals(consumeMessageOrderly);
+    }
+
+    public boolean isBroadcastConsume() {
+        return Boolean.TRUE.equals(consumeBroadcastEnable);
+    }
+    public String getConsumerGroupName() {
+        return consumerGroupName;
+    }
+
+    public void setConsumerGroupName(String consumerGroupName) {
+        this.consumerGroupName = consumerGroupName;
+    }
+
+    public String getConsumeMode() {
+        return consumeMode;
+    }
+
+    public void setConsumeMode(String consumeMode) {
+        this.consumeMode = consumeMode;
+    }
+
+    public Boolean getConsumeMessageOrderly() {
+        return consumeMessageOrderly;
+    }
+
+    public void setConsumeMessageOrderly(Boolean consumeMessageOrderly) {
+        this.consumeMessageOrderly = consumeMessageOrderly;
+    }
+
+    public Boolean getConsumeBroadcastEnable() {
+        return consumeBroadcastEnable;
+    }
+
+    public void setConsumeBroadcastEnable(Boolean consumeBroadcastEnable) {
+        this.consumeBroadcastEnable = consumeBroadcastEnable;
+    }
+
+    public Boolean getConsumeFromMinEnable() {
+        return consumeFromMinEnable;
+    }
+
+    public void setConsumeFromMinEnable(Boolean consumeFromMinEnable) {
+        this.consumeFromMinEnable = consumeFromMinEnable;
+    }
+
+    public Integer getRetryQueueNums() {
+        return retryQueueNums;
+    }
+
+    public void setRetryQueueNums(Integer retryQueueNums) {
+        this.retryQueueNums = retryQueueNums;
+    }
+
+    public Integer getRetryMaxTimes() {
+        return retryMaxTimes;
+    }
+
+    public void setRetryMaxTimes(Integer retryMaxTimes) {
+        this.retryMaxTimes = retryMaxTimes;
+    }
+
+    public Integer getConsumeTimeoutMinute() {
+        return consumeTimeoutMinute;
+    }
+
+    public void setConsumeTimeoutMinute(Integer consumeTimeoutMinute) {
+        this.consumeTimeoutMinute = consumeTimeoutMinute;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Set<String> getSubscribedTopics() {
+        return subscribedTopics;
+    }
+
+    public void setSubscribedTopics(Set<String> subscribedTopics) {
+        this.subscribedTopics = subscribedTopics;
+    }
+
+    public Integer getOnlineClientCount() {
+        return onlineClientCount;
+    }
+
+    public void setOnlineClientCount(Integer onlineClientCount) {
+        this.onlineClientCount = onlineClientCount;
+    }
+
+    public String getLiteBindTopic() {
+        return liteBindTopic;
+    }
+
+    public void setLiteBindTopic(String liteBindTopic) {
+        this.liteBindTopic = liteBindTopic;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public Integer getGroupSysFlag() {
+        return groupSysFlag;
+    }
+
+    public void setGroupSysFlag(Integer groupSysFlag) {
+        this.groupSysFlag = groupSysFlag;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerMonitorConfig.java b/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerMonitorConfig.java
new file mode 100644
index 00000000..9bb74c3a
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/ConsumerMonitorConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+public class ConsumerMonitorConfig {
+    private int minCount;
+    private int maxDiffTotal;
+
+    public ConsumerMonitorConfig() {
+    }
+
+    public ConsumerMonitorConfig(int minCount, int maxDiffTotal) {
+        this.minCount = minCount;
+        this.maxDiffTotal = maxDiffTotal;
+    }
+
+    public int getMinCount() {
+        return minCount;
+    }
+
+    public void setMinCount(int minCount) {
+        this.minCount = minCount;
+    }
+
+    public int getMaxDiffTotal() {
+        return maxDiffTotal;
+    }
+
+    public void setMaxDiffTotal(int maxDiffTotal) {
+        this.maxDiffTotal = maxDiffTotal;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/Entry.java b/server/src/main/java/org/apache/rocketmq/studio/model/Entry.java
new file mode 100644
index 00000000..8b623ede
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/Entry.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model;
+
+
+import java.util.List;
+
+public class Entry {
+    private List<String> resource;
+    private List<String> actions;
+    private List<String> sourceIps;
+    private String decision;
+
+    public List<String> getResource() {
+        return resource;
+    }
+
+    public void setResource(List<String> resource) {
+        this.resource = resource;
+    }
+
+    public List<String> getActions() {
+        return actions;
+    }
+
+    public void setActions(List<String> actions) {
+        this.actions = actions;
+    }
+
+    public List<String> getSourceIps() {
+        return sourceIps;
+    }
+
+    public void setSourceIps(List<String> sourceIps) {
+        this.sourceIps = sourceIps;
+    }
+
+    public String getDecision() {
+        return decision;
+    }
+
+    public void setDecision(String decision) {
+        this.decision = decision;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicQuota.java b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicQuota.java
new file mode 100644
index 00000000..daa39b8c
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicQuota.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+
+public class LiteTopicQuota {
+
+    private Integer maxTopicCount;
+
+    private Integer currentTopicCount;
+
+    private Integer maxSessionCount;
+
+    private Integer currentSessionCount;
+
+    private Long defaultTTL;
+
+    private Long maxTTL;
+
+    private Double currentCreationRate;
+
+    private Double maxCreationRate;
+
+    public double getUsageRate() {
+        if (maxTopicCount == null || maxTopicCount == 0) {
+            return 0.0;
+        }
+        return (double) currentTopicCount / maxTopicCount;
+    }
+
+    public double getSessionUsageRate() {
+        if (maxSessionCount == null || maxSessionCount == 0) {
+            return 0.0;
+        }
+        return (double) currentSessionCount / maxSessionCount;
+    }
+
+    public boolean isNearQuotaLimit(double threshold) {
+        return getUsageRate() >= threshold;
+    }
+
+    public boolean isQuotaExceeded() {
+        return currentTopicCount >= maxTopicCount;
+    }
+
+    public Integer getRemainingQuota() {
+        if (maxTopicCount == null) {
+            return 0;
+        }
+        return Math.max(0, maxTopicCount - currentTopicCount);
+    }
+    public Integer getMaxTopicCount() {
+        return maxTopicCount;
+    }
+
+    public void setMaxTopicCount(Integer maxTopicCount) {
+        this.maxTopicCount = maxTopicCount;
+    }
+
+    public Integer getCurrentTopicCount() {
+        return currentTopicCount;
+    }
+
+    public void setCurrentTopicCount(Integer currentTopicCount) {
+        this.currentTopicCount = currentTopicCount;
+    }
+
+    public Integer getMaxSessionCount() {
+        return maxSessionCount;
+    }
+
+    public void setMaxSessionCount(Integer maxSessionCount) {
+        this.maxSessionCount = maxSessionCount;
+    }
+
+    public Integer getCurrentSessionCount() {
+        return currentSessionCount;
+    }
+
+    public void setCurrentSessionCount(Integer currentSessionCount) {
+        this.currentSessionCount = currentSessionCount;
+    }
+
+    public Long getDefaultTTL() {
+        return defaultTTL;
+    }
+
+    public void setDefaultTTL(Long defaultTTL) {
+        this.defaultTTL = defaultTTL;
+    }
+
+    public Long getMaxTTL() {
+        return maxTTL;
+    }
+
+    public void setMaxTTL(Long maxTTL) {
+        this.maxTTL = maxTTL;
+    }
+
+    public Double getCurrentCreationRate() {
+        return currentCreationRate;
+    }
+
+    public void setCurrentCreationRate(Double currentCreationRate) {
+        this.currentCreationRate = currentCreationRate;
+    }
+
+    public Double getMaxCreationRate() {
+        return maxCreationRate;
+    }
+
+    public void setMaxCreationRate(Double maxCreationRate) {
+        this.maxCreationRate = maxCreationRate;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSession.java b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSession.java
new file mode 100644
index 00000000..f7ea3587
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSession.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Set;
+
+public class LiteTopicSession {
+
+    /**
+     * Session ID
+     */
+    private String sessionId;
+
+    private String clientId;
+
+    private String clientAddress;
+
+    private Set<String> liteTopics;
+
+    private String parentTopic;
+
+    /**
+     * Consumer Group
+     */
+    private String consumerGroup;
+
+    private Date createTime;
+
+    private Date lastActiveTime;
+
+    private Long ttl;
+
+    private Long ttlRemaining;
+
+    private String status;
+
+    private Long totalMessages;
+
+    private Long consumedMessages;
+
+    private Long pendingMessages;
+
+    private Double consumptionRate;
+
+    private PopConsumeProgress popProgress;
+
+    private Integer liteTopicCreationCount;
+
+    public boolean hasActiveConsumption() {
+        return "ACTIVE".equals(status) && consumptionRate != null && consumptionRate > 0;
+    }
+
+    public boolean isExpired() {
+        return "EXPIRED".equals(status) || ttlRemaining != null && ttlRemaining <= 0;
+    }
+
+    public double getConsumptionProgress() {
+        if (totalMessages == null || totalMessages == 0) {
+            return 0.0;
+        }
+        return (double) consumedMessages / totalMessages * 100.0;
+    }
+
+    public static class PopConsumeProgress {
+        private Integer ackTimeoutSeconds;
+        private Integer maxReconsumeTimes;
+        private Integer totalPopInFlightCount;
+        private Integer lastPopTime;
+        private Integer popCheckpoint;
+        private Integer totalPopCount;
+    }
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getClientAddress() {
+        return clientAddress;
+    }
+
+    public void setClientAddress(String clientAddress) {
+        this.clientAddress = clientAddress;
+    }
+
+    public Set<String> getLiteTopics() {
+        return liteTopics;
+    }
+
+    public void setLiteTopics(Set<String> liteTopics) {
+        this.liteTopics = liteTopics;
+    }
+
+    public String getParentTopic() {
+        return parentTopic;
+    }
+
+    public void setParentTopic(String parentTopic) {
+        this.parentTopic = parentTopic;
+    }
+
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getLastActiveTime() {
+        return lastActiveTime;
+    }
+
+    public void setLastActiveTime(Date lastActiveTime) {
+        this.lastActiveTime = lastActiveTime;
+    }
+
+    public Long getTtl() {
+        return ttl;
+    }
+
+    public void setTtl(Long ttl) {
+        this.ttl = ttl;
+    }
+
+    public Long getTtlRemaining() {
+        return ttlRemaining;
+    }
+
+    public void setTtlRemaining(Long ttlRemaining) {
+        this.ttlRemaining = ttlRemaining;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Long getTotalMessages() {
+        return totalMessages;
+    }
+
+    public void setTotalMessages(Long totalMessages) {
+        this.totalMessages = totalMessages;
+    }
+
+    public Long getConsumedMessages() {
+        return consumedMessages;
+    }
+
+    public void setConsumedMessages(Long consumedMessages) {
+        this.consumedMessages = consumedMessages;
+    }
+
+    public Long getPendingMessages() {
+        return pendingMessages;
+    }
+
+    public void setPendingMessages(Long pendingMessages) {
+        this.pendingMessages = pendingMessages;
+    }
+
+    public Double getConsumptionRate() {
+        return consumptionRate;
+    }
+
+    public void setConsumptionRate(Double consumptionRate) {
+        this.consumptionRate = consumptionRate;
+    }
+
+    public PopConsumeProgress getPopProgress() {
+        return popProgress;
+    }
+
+    public void setPopProgress(PopConsumeProgress popProgress) {
+        this.popProgress = popProgress;
+    }
+
+    public Integer getLiteTopicCreationCount() {
+        return liteTopicCreationCount;
+    }
+
+    public void setLiteTopicCreationCount(Integer liteTopicCreationCount) {
+        this.liteTopicCreationCount = liteTopicCreationCount;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSummary.java b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSummary.java
new file mode 100644
index 00000000..49447ac5
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/LiteTopicSummary.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.List;
+
+public class LiteTopicSummary {
+
+    private String topicPattern;
+
+    private Integer topicCount;
+
+    private List<String> sessionIds;
+
+    private Date earliestCreateTime;
+
+    private Date lastActiveTime;
+
+    private Long averageTTL;
+
+    private Long minTTL;
+
+    private Long maxTTL;
+
+    private Integer consumerCount;
+
+    private Long totalBacklog;
+
+    private boolean active;
+
+    private java.util.Map<String, Object> attributes;
+
+    public String getTTLStatus() {
+        if (lastActiveTime == null) {
+            return "UNKNOWN";
+        }
+
+        long now = System.currentTimeMillis();
+        long elapsed = now - lastActiveTime.getTime();
+
+        if (averageTTL != null && elapsed > averageTTL * 0.8) {
+            return "EXPIRING_SOON";
+        } else if (averageTTL != null && elapsed > averageTTL) {
+            return "EXPIRED";
+        } else {
+            return "ACTIVE";
+        }
+    }
+
+    public double getConsumerDensity() {
+        if (topicCount == null || topicCount == 0) {
+            return 0.0;
+        }
+        return (double) consumerCount / topicCount;
+    }
+
+    public boolean isEmptyAggregation() {
+        return consumerCount == 0 && (totalBacklog == null || totalBacklog == 0);
+    }
+    public String getTopicPattern() {
+        return topicPattern;
+    }
+
+    public void setTopicPattern(String topicPattern) {
+        this.topicPattern = topicPattern;
+    }
+
+    public Integer getTopicCount() {
+        return topicCount;
+    }
+
+    public void setTopicCount(Integer topicCount) {
+        this.topicCount = topicCount;
+    }
+
+    public List<String> getSessionIds() {
+        return sessionIds;
+    }
+
+    public void setSessionIds(List<String> sessionIds) {
+        this.sessionIds = sessionIds;
+    }
+
+    public Date getEarliestCreateTime() {
+        return earliestCreateTime;
+    }
+
+    public void setEarliestCreateTime(Date earliestCreateTime) {
+        this.earliestCreateTime = earliestCreateTime;
+    }
+
+    public Date getLastActiveTime() {
+        return lastActiveTime;
+    }
+
+    public void setLastActiveTime(Date lastActiveTime) {
+        this.lastActiveTime = lastActiveTime;
+    }
+
+    public Long getAverageTTL() {
+        return averageTTL;
+    }
+
+    public void setAverageTTL(Long averageTTL) {
+        this.averageTTL = averageTTL;
+    }
+
+    public Long getMinTTL() {
+        return minTTL;
+    }
+
+    public void setMinTTL(Long minTTL) {
+        this.minTTL = minTTL;
+    }
+
+    public Long getMaxTTL() {
+        return maxTTL;
+    }
+
+    public void setMaxTTL(Long maxTTL) {
+        this.maxTTL = maxTTL;
+    }
+
+    public Integer getConsumerCount() {
+        return consumerCount;
+    }
+
+    public void setConsumerCount(Integer consumerCount) {
+        this.consumerCount = consumerCount;
+    }
+
+    public Long getTotalBacklog() {
+        return totalBacklog;
+    }
+
+    public void setTotalBacklog(Long totalBacklog) {
+        this.totalBacklog = totalBacklog;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/LoginInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/LoginInfo.java
new file mode 100644
index 00000000..189c6928
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/LoginInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model;
+
+public class LoginInfo {
+    private boolean loginRequired;
+    private boolean logined;
+
+    public boolean isLoginRequired() {
+        return loginRequired;
+    }
+
+    public void setLoginRequired(boolean loginRequired) {
+        this.loginRequired = loginRequired;
+    }
+
+    public boolean isLogined() {
+        return logined;
+    }
+
+    public void setLogined(boolean logined) {
+        this.logined = logined;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/LoginResult.java b/server/src/main/java/org/apache/rocketmq/studio/model/LoginResult.java
new file mode 100644
index 00000000..72eccf1f
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/LoginResult.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+
+public class LoginResult {
+
+    private String loginUserName;
+
+    /**
+     * 0: normal 1: admin
+     */
+    private int loginUserRole;
+
+    private String contextPath;
+    public String getLoginUserName() {
+        return loginUserName;
+    }
+
+    public void setLoginUserName(String loginUserName) {
+        this.loginUserName = loginUserName;
+    }
+
+    public int getLoginUserRole() {
+        return loginUserRole;
+    }
+
+    public void setLoginUserRole(int loginUserRole) {
+        this.loginUserRole = loginUserRole;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/MessageInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/MessageInfo.java
new file mode 100644
index 00000000..26c0f42b
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/MessageInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Map;
+
+/**
+ * Message information model for message query and management.
+ * Encapsulates key message attributes for dashboard display.
+ */
+public class MessageInfo {
+
+    private String msgId;
+    private String topic;
+    private String brokerName;
+    private int queueId;
+    private long queueOffset;
+    private long storeTimestamp;
+    private long bornTimestamp;
+    private String keys;
+    private String tags;
+    private String body;
+    private Map<String, String> properties;
+    private boolean retryTopic;
+    private int reconsumeTimes;
+
+    public String getMsgId() {
+        return msgId;
+    }
+
+    public void setMsgId(String msgId) {
+        this.msgId = msgId;
+    }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public int getQueueId() {
+        return queueId;
+    }
+
+    public void setQueueId(int queueId) {
+        this.queueId = queueId;
+    }
+
+    public long getQueueOffset() {
+        return queueOffset;
+    }
+
+    public void setQueueOffset(long queueOffset) {
+        this.queueOffset = queueOffset;
+    }
+
+    public long getStoreTimestamp() {
+        return storeTimestamp;
+    }
+
+    public void setStoreTimestamp(long storeTimestamp) {
+        this.storeTimestamp = storeTimestamp;
+    }
+
+    public long getBornTimestamp() {
+        return bornTimestamp;
+    }
+
+    public void setBornTimestamp(long bornTimestamp) {
+        this.bornTimestamp = bornTimestamp;
+    }
+
+    public String getKeys() {
+        return keys;
+    }
+
+    public void setKeys(String keys) {
+        this.keys = keys;
+    }
+
+    public String getTags() {
+        return tags;
+    }
+
+    public void setTags(String tags) {
+        this.tags = tags;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+
+    public boolean isRetryTopic() {
+        return retryTopic;
+    }
+
+    public void setRetryTopic(boolean retryTopic) {
+        this.retryTopic = retryTopic;
+    }
+
+    public int getReconsumeTimes() {
+        return reconsumeTimes;
+    }
+
+    public void setReconsumeTimes(int reconsumeTimes) {
+        this.reconsumeTimes = reconsumeTimes;
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/MetricsDataSourceConfig.java b/server/src/main/java/org/apache/rocketmq/studio/model/MetricsDataSourceConfig.java
new file mode 100644
index 00000000..f15400ae
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/MetricsDataSourceConfig.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Configuration model for Prometheus-compatible metrics data sources.
+ * Supports multiple data sources (N:N mapping with clusters).
+ * Auth types: none, basic, bearer, sigv4.
+ * <p>
+ * This class carries raw configuration values. Sensitive fields such as
+ * password / bearerToken should be encrypted at rest in production.
+ * </p>
+ */
+public class MetricsDataSourceConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * User-defined name identifying this data source.
+     */
+    private String name;
+
+    /**
+     * Base URL of the Prometheus-compatible backend
+     * (e.g., http://prometheus:9090).
+     */
+    private String url;
+
+    /**
+     * Authentication type: "none", "basic", "bearer", "sigv4".
+     */
+    private String authType;
+
+    /**
+     * Username for basic authentication.
+     */
+    private String username;
+
+    /**
+     * Password for basic authentication (encrypted in storage).
+     */
+    private String password;
+
+    /**
+     * Bearer token for bearer authentication.
+     */
+    private String bearerToken;
+
+    /**
+     * Prometheus-compatible backend provider type.
+     * Supported: PROMETHEUS, VICTORIAMETRICS, THANOS, MIMIR, CORTEX, ARMS, CUSTOM
+     */
+    private String providerType = "PROMETHEUS";
+
+    /**
+     * Whether TLS / HTTPS is enabled.
+     */
+    private boolean tlsEnabled;
+
+    /**
+     * Default labels used for PromQL construction (e.g., cluster -> broker name).
+     */
+    private Map<String, String> defaultLabels;
+
+    /**
+     * Scrape interval in seconds configured on the Prometheus side.
+     */
+    private int scrapeInterval;
+
+    /**
+     * Whether this data source is currently activated.
+     */
+    private boolean enabled;
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getAuthType() {
+        return authType;
+    }
+
+    public void setAuthType(String authType) {
+        this.authType = authType;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getBearerToken() {
+        return bearerToken;
+    }
+
+    public void setBearerToken(String bearerToken) {
+        this.bearerToken = bearerToken;
+    }
+
+    public String getProviderType() {
+        return providerType;
+    }
+
+    public void setProviderType(String providerType) {
+        this.providerType = providerType;
+    }
+
+    public boolean isTlsEnabled() {
+        return tlsEnabled;
+    }
+
+    public void setTlsEnabled(boolean tlsEnabled) {
+        this.tlsEnabled = tlsEnabled;
+    }
+
+    public Map<String, String> getDefaultLabels() {
+        return defaultLabels;
+    }
+
+    public void setDefaultLabels(Map<String, String> defaultLabels) {
+        this.defaultLabels = defaultLabels;
+    }
+
+    public int getScrapeInterval() {
+        return scrapeInterval;
+    }
+
+    public void setScrapeInterval(int scrapeInterval) {
+        this.scrapeInterval = scrapeInterval;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/MetricsHealthResult.java b/server/src/main/java/org/apache/rocketmq/studio/model/MetricsHealthResult.java
new file mode 100644
index 00000000..118ff31e
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/MetricsHealthResult.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Result object returned by data-source health checks.
+ * Contains connection status, discovered metric families, and latency.
+ */
+public class MetricsHealthResult implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** Whether the backend was reachable and responded successfully. */
+    private boolean connected;
+
+    /** Human-readable status message (e.g., error description). */
+    private String statusMessage;
+
+    /** Metric-family names that are expected but NOT found in the data source. */
+    private List<String> missingMetricFamilies;
+
+    /** Metric-family names actually discovered via /api/v1/labels or /api/v1/targets. */
+    private List<String> availableMetricFamilies;
+
+    /** Round-trip query latency in milliseconds during the health probe. */
+    private long queryLatencyMs;
+
+
+    public boolean isConnected() {
+        return connected;
+    }
+
+    public void setConnected(boolean connected) {
+        this.connected = connected;
+    }
+
+    public String getStatusMessage() {
+        return statusMessage;
+    }
+
+    public void setStatusMessage(String statusMessage) {
+        this.statusMessage = statusMessage;
+    }
+
+    public List<String> getMissingMetricFamilies() {
+        return missingMetricFamilies;
+    }
+
+    public void setMissingMetricFamilies(List<String> missingMetricFamilies) {
+        this.missingMetricFamilies = missingMetricFamilies;
+    }
+
+    public List<String> getAvailableMetricFamilies() {
+        return availableMetricFamilies;
+    }
+
+    public void setAvailableMetricFamilies(List<String> availableMetricFamilies) {
+        this.availableMetricFamilies = availableMetricFamilies;
+    }
+
+    public long getQueryLatencyMs() {
+        return queryLatencyMs;
+    }
+
+    public void setQueryLatencyMs(long queryLatencyMs) {
+        this.queryLatencyMs = queryLatencyMs;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/Policy.java b/server/src/main/java/org/apache/rocketmq/studio/model/Policy.java
new file mode 100644
index 00000000..2483425e
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/Policy.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model;
+
+
+import java.util.List;
+
+public class Policy {
+    private String policyType;
+    private List<Entry> entries;
+    public String getPolicyType() {
+        return policyType;
+    }
+
+    public void setPolicyType(String policyType) {
+        this.policyType = policyType;
+    }
+
+    public List<Entry> getEntries() {
+        return entries;
+    }
+
+    public void setEntries(List<Entry> entries) {
+        this.entries = entries;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/PolicyRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/PolicyRequest.java
new file mode 100644
index 00000000..b143812e
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/PolicyRequest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model;
+
+
+import java.util.List;
+
+public class PolicyRequest {
+    private String clusterName;
+    private String brokerName;
+    private String subject;
+    private List<Policy> policies;
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public List<Policy> getPolicies() {
+        return policies;
+    }
+
+    public void setPolicies(List<Policy> policies) {
+        this.policies = policies;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/SubscriptionInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/SubscriptionInfo.java
new file mode 100644
index 00000000..ccc3bd59
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/SubscriptionInfo.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Map;
+
+public class SubscriptionInfo {
+
+    private String topic;
+
+    private String consumerGroup;
+
+    private String subExpression;
+
+    private String subscriptionType;
+
+    private Long version;
+
+    private Boolean broadcast;
+
+    private String consumeFromWhere;
+
+    private Long consumeProgress;
+
+    private Long backlogCount;
+
+    private Map<String, Object> attributes;
+
+    public boolean isTagSubscription() {
+        return "TAG".equals(subscriptionType);
+    }
+
+    public boolean isSQL92Subscription() {
+        return "SQL92".equals(subscriptionType);
+    }
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
+    public String getSubExpression() {
+        return subExpression;
+    }
+
+    public void setSubExpression(String subExpression) {
+        this.subExpression = subExpression;
+    }
+
+    public String getSubscriptionType() {
+        return subscriptionType;
+    }
+
+    public void setSubscriptionType(String subscriptionType) {
+        this.subscriptionType = subscriptionType;
+    }
+
+    public Long getVersion() {
+        return version;
+    }
+
+    public void setVersion(Long version) {
+        this.version = version;
+    }
+
+    public Boolean getBroadcast() {
+        return broadcast;
+    }
+
+    public void setBroadcast(Boolean broadcast) {
+        this.broadcast = broadcast;
+    }
+
+    public String getConsumeFromWhere() {
+        return consumeFromWhere;
+    }
+
+    public void setConsumeFromWhere(String consumeFromWhere) {
+        this.consumeFromWhere = consumeFromWhere;
+    }
+
+    public Long getConsumeProgress() {
+        return consumeProgress;
+    }
+
+    public void setConsumeProgress(Long consumeProgress) {
+        this.consumeProgress = consumeProgress;
+    }
+
+    public Long getBacklogCount() {
+        return backlogCount;
+    }
+
+    public void setBacklogCount(Long backlogCount) {
+        this.backlogCount = backlogCount;
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, Object> attributes) {
+        this.attributes = attributes;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/TopicInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/TopicInfo.java
new file mode 100644
index 00000000..bf5082cb
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/TopicInfo.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Map;
+
+public class TopicInfo {
+
+    private String topicName;
+
+    private TopicType topicType;
+
+    private Integer readQueueNums;
+
+    private Integer writeQueueNums;
+
+    private Integer perm;
+
+    private Boolean orderTopic;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String topicStatus;
+
+    private String clusterName;
+
+    private Map<String, String> attributes;
+
+    private Long fifoTimeoutSeconds;
+
+    private Long liteTopicTTL;
+
+    /**
+     * LiteTopic - Session ID
+     */
+    private String sessionId;
+
+    private String autoCreatePattern;
+
+    private String delayLevel;
+
+    private String transactionServerAddr;
+
+    private Long transactionTimeoutSeconds;
+
+    public String getDisplayName() {
+        return topicName;
+    }
+
+    public boolean isLiteTopic() {
+        return TopicType.LITE.equals(topicType);
+    }
+
+    public boolean isOrderTopic() {
+        return Boolean.TRUE.equals(orderTopic) || TopicType.FIFO.equals(topicType);
+    }
+
+    public boolean isDelayTopic() {
+        return TopicType.DELAY.equals(topicType);
+    }
+
+    public boolean isTransactionTopic() {
+        return TopicType.TRANSACTION.equals(topicType);
+    }
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public void setTopicName(String topicName) {
+        this.topicName = topicName;
+    }
+
+    public TopicType getTopicType() {
+        return topicType;
+    }
+
+    public void setTopicType(TopicType topicType) {
+        this.topicType = topicType;
+    }
+
+    public Integer getReadQueueNums() {
+        return readQueueNums;
+    }
+
+    public void setReadQueueNums(Integer readQueueNums) {
+        this.readQueueNums = readQueueNums;
+    }
+
+    public Integer getWriteQueueNums() {
+        return writeQueueNums;
+    }
+
+    public void setWriteQueueNums(Integer writeQueueNums) {
+        this.writeQueueNums = writeQueueNums;
+    }
+
+    public Integer getPerm() {
+        return perm;
+    }
+
+    public void setPerm(Integer perm) {
+        this.perm = perm;
+    }
+
+    public Boolean getOrderTopic() {
+        return orderTopic;
+    }
+
+    public void setOrderTopic(Boolean orderTopic) {
+        this.orderTopic = orderTopic;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getTopicStatus() {
+        return topicStatus;
+    }
+
+    public void setTopicStatus(String topicStatus) {
+        this.topicStatus = topicStatus;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public Long getFifoTimeoutSeconds() {
+        return fifoTimeoutSeconds;
+    }
+
+    public void setFifoTimeoutSeconds(Long fifoTimeoutSeconds) {
+        this.fifoTimeoutSeconds = fifoTimeoutSeconds;
+    }
+
+    public Long getLiteTopicTTL() {
+        return liteTopicTTL;
+    }
+
+    public void setLiteTopicTTL(Long liteTopicTTL) {
+        this.liteTopicTTL = liteTopicTTL;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    public String getAutoCreatePattern() {
+        return autoCreatePattern;
+    }
+
+    public void setAutoCreatePattern(String autoCreatePattern) {
+        this.autoCreatePattern = autoCreatePattern;
+    }
+
+    public String getDelayLevel() {
+        return delayLevel;
+    }
+
+    public void setDelayLevel(String delayLevel) {
+        this.delayLevel = delayLevel;
+    }
+
+    public String getTransactionServerAddr() {
+        return transactionServerAddr;
+    }
+
+    public void setTransactionServerAddr(String transactionServerAddr) {
+        this.transactionServerAddr = transactionServerAddr;
+    }
+
+    public Long getTransactionTimeoutSeconds() {
+        return transactionTimeoutSeconds;
+    }
+
+    public void setTransactionTimeoutSeconds(Long transactionTimeoutSeconds) {
+        this.transactionTimeoutSeconds = transactionTimeoutSeconds;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/TopicType.java b/server/src/main/java/org/apache/rocketmq/studio/model/TopicType.java
new file mode 100644
index 00000000..9e12fb24
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/TopicType.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+public enum TopicType {
+
+    NORMAL("NORMAL", "Normal message", false),
+
+    FIFO("FIFO", "FIFO ordered message", true),
+
+    DELAY("DELAY", "Delay message", true),
+
+    TRANSACTION("TRANSACTION", "Transaction message", true),
+
+    LITE("LITE", "LiteTopic", true);
+
+    private final String value;
+    private final String description;
+    private final boolean requiresSpecialConfig;
+
+    TopicType(String value, String description, boolean requiresSpecialConfig) {
+        this.value = value;
+        this.description = description;
+        this.requiresSpecialConfig = requiresSpecialConfig;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public boolean isRequiresSpecialConfig() {
+        return requiresSpecialConfig;
+    }
+
+    public boolean isV5Specific() {
+        return this == LITE;
+    }
+
+    public boolean isNotSupportedInV4() {
+        return this == LITE;
+    }
+
+    public static TopicType fromValue(String value) {
+        for (TopicType type : values()) {
+            if (type.getValue().equals(value)) {
+                return type;
+            }
+        }
+        throw new IllegalArgumentException("Unknown topic type: " + value);
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/User.java b/server/src/main/java/org/apache/rocketmq/studio/model/User.java
new file mode 100644
index 00000000..020aed11
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/User.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import org.hibernate.validator.constraints.Range;
+
+public class User {
+    public static final int SUPER = 0;
+    public static final int NORMAL = 1;
+
+    private long id;
+    private String name;
+    private String password;
+    @Range(min = 0, max = 1)
+    private int type = 0;
+
+
+    public User(String name, String password, int type) {
+        this.name = name;
+        this.password = password;
+        this.type = type;
+    }
+
+    public User cloneOne() {
+        return new User(this.name, this.password, this.type);
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        return "User{" +
+                "id=" + id +
+                ", name='" + name + '\'' +
+                ", password='" + password + '\'' +
+                ", type=" + type +
+                '}';
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/UserInfo.java b/server/src/main/java/org/apache/rocketmq/studio/model/UserInfo.java
new file mode 100644
index 00000000..b5f21236
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/UserInfo.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+public class UserInfo {
+    public static final String USER_INFO = "userInfo";
+    private User user;
+    private long loginTime;
+    private String ip;
+    private String sessionId;
+
+    public long getLoginTime() {
+        return loginTime;
+    }
+
+    public void setLoginTime(long loginTime) {
+        this.loginTime = loginTime;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    @Override
+    public String toString() {
+        return "UserInfo{" +
+                "user=" + user +
+                ", loginTime=" + loginTime +
+                ", ip='" + ip + '\'' +
+                ", sessionId='" + sessionId + '\'' +
+                '}';
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/ArchitectureSwitchRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/ArchitectureSwitchRequest.java
new file mode 100644
index 00000000..574eb099
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/ArchitectureSwitchRequest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+/**
+ * Request body for architecture switch operation.
+ *
+ * <p>Used by {@code POST /api/architecture/switch} to dynamically switch
+ * the dashboard between different cluster architecture types.</p>
+ *
+ * <p>For V5 architectures (V5_PROXY_LOCAL, V5_PROXY_CLUSTER), the
+ * {@code proxyAddresses} and {@code nameSrvAddress} fields are required.
+ * For V4 architecture, only {@code accessType} is needed.</p>
+ */
+public class ArchitectureSwitchRequest {
+
+    /**
+     * Target access type name (e.g., "V4_NAMESRV", "V5_PROXY_LOCAL", "V5_PROXY_CLUSTER").
+     * Must match a value in {@link org.apache.rocketmq.studio.architecture.ClusterAccessType}.
+     */
+    private String accessType;
+
+    /**
+     * Proxy node addresses for V5 architecture.
+     * Required when accessType is V5_PROXY_LOCAL or V5_PROXY_CLUSTER.
+     */
+    private String[] proxyAddresses;
+
+    /**
+     * NameServer address for Remoting fallback.
+     * Required when accessType is V5_PROXY_LOCAL or V5_PROXY_CLUSTER.
+     */
+    private String nameSrvAddress;
+
+    public String getAccessType() {
+        return accessType;
+    }
+
+    public void setAccessType(String accessType) {
+        this.accessType = accessType;
+    }
+
+    public String[] getProxyAddresses() {
+        return proxyAddresses;
+    }
+
+    public void setProxyAddresses(String[] proxyAddresses) {
+        this.proxyAddresses = proxyAddresses;
+    }
+
+    public String getNameSrvAddress() {
+        return nameSrvAddress;
+    }
+
+    public void setNameSrvAddress(String nameSrvAddress) {
+        this.nameSrvAddress = nameSrvAddress;
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/MessageQuery.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/MessageQuery.java
new file mode 100644
index 00000000..141ce421
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/MessageQuery.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+public class MessageQuery {
+    /**
+     * current page num
+     */
+    private int pageNum;
+
+    private int pageSize;
+
+    private String topic;
+
+    private String taskId;
+
+    private long begin;
+
+    private long end;
+
+    public int getPageNum() {
+        return pageNum;
+    }
+
+    public void setPageNum(int pageNum) {
+        this.pageNum = pageNum;
+    }
+
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(String taskId) {
+        this.taskId = taskId;
+    }
+
+    public long getBegin() {
+        return begin;
+    }
+
+    public void setBegin(long begin) {
+        this.begin = begin;
+    }
+
+    public long getEnd() {
+        return end;
+    }
+
+    public void setEnd(long end) {
+        this.end = end;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceRequest.java
new file mode 100644
index 00000000..72846b2b
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceRequest.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+
+/**
+ * Metrics data source configuration request DTO.
+ *
+ * <p>Used for creating and updating Prometheus-compatible data source
+ * configurations for the PromQL proxy query feature.</p>
+ *
+ * <h3>Supported Data Source Types</h3>
+ * <ul>
+ *   <li><b>PROMETHEUS:</b> Standard Prometheus HTTP API server</li>
+ *   <li><b>VICTORIAMETRICS:</b> VictoriaMetrics (Prometheus-compatible)</li>
+ *   <li><b>THANOS:</b> Thanos Query (Prometheus-compatible)</li>
+ *   <li><b>CORTEX:</b> Cortex (Prometheus-compatible)</li>
+ * </ul>
+ */
+public class MetricsDataSourceRequest {
+
+    /**
+     * Data source display name (required for create).
+     */
+    private String name;
+
+    /**
+     * Data source type (PROMETHEUS, VICTORIAMETRICS, THANOS, CORTEX).
+     * Defaults to PROMETHEUS if not specified.
+     */
+    private String type;
+
+    /**
+     * Prometheus-compatible backend provider type.
+     * Supported: PROMETHEUS, VICTORIAMETRICS, THANOS, MIMIR, CORTEX, ARMS, CUSTOM
+     */
+    private String providerType = "PROMETHEUS";
+
+    /**
+     * Data source URL (required for create).
+     * Example: "http://prometheus:9090"
+     */
+    private String url;
+
+    /**
+     * Authentication username (optional).
+     */
+    private String username;
+
+    /**
+     * Authentication password (optional).
+     */
+    private String password;
+
+    /**
+     * Bearer token for authentication (optional).
+     * If set, takes precedence over username/password.
+     */
+    private String bearerToken;
+
+    /**
+     * Whether this data source is the default for PromQL queries.
+     * Only one data source can be the default at a time.
+     */
+    private boolean isDefault;
+
+    /**
+     * Whether this data source is read-only.
+     * Read-only data sources only support query operations.
+     */
+    private boolean readOnly;
+
+    /**
+     * Custom HTTP headers to include in requests to this data source.
+     * Format: "Header-Name: Header-Value" per line.
+     */
+    private String customHeaders;
+
+    /**
+     * Connection timeout in milliseconds (default: 5000).
+     */
+    private Integer connectionTimeoutMs;
+
+    /**
+     * Read timeout in milliseconds (default: 30000).
+     */
+    private Integer readTimeoutMs;
+
+    /**
+     * Additional description or notes about this data source.
+     */
+    private String description;
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getProviderType() {
+        return providerType;
+    }
+
+    public void setProviderType(String providerType) {
+        this.providerType = providerType;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getBearerToken() {
+        return bearerToken;
+    }
+
+    public void setBearerToken(String bearerToken) {
+        this.bearerToken = bearerToken;
+    }
+
+    public boolean isIsDefault() {
+        return isDefault;
+    }
+
+    public void setIsDefault(boolean isDefault) {
+        this.isDefault = isDefault;
+    }
+
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    public void setReadOnly(boolean readOnly) {
+        this.readOnly = readOnly;
+    }
+
+    public String getCustomHeaders() {
+        return customHeaders;
+    }
+
+    public void setCustomHeaders(String customHeaders) {
+        this.customHeaders = customHeaders;
+    }
+
+    public Integer getConnectionTimeoutMs() {
+        return connectionTimeoutMs;
+    }
+
+    public void setConnectionTimeoutMs(Integer connectionTimeoutMs) {
+        this.connectionTimeoutMs = connectionTimeoutMs;
+    }
+
+    public Integer getReadTimeoutMs() {
+        return readTimeoutMs;
+    }
+
+    public void setReadTimeoutMs(Integer readTimeoutMs) {
+        this.readTimeoutMs = readTimeoutMs;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/ResetOffsetRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/ResetOffsetRequest.java
new file mode 100644
index 00000000..f741dd3f
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/ResetOffsetRequest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+import java.util.List;
+
+public class ResetOffsetRequest {
+    private List<String> consumerGroupList;
+    private String topic;
+    private long resetTime;
+    private boolean force;
+
+    public List<String> getConsumerGroupList() {
+        return consumerGroupList;
+    }
+
+    public void setConsumerGroupList(List<String> consumerGroupList) {
+        this.consumerGroupList = consumerGroupList;
+    }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public long getResetTime() {
+        return resetTime;
+    }
+
+    public void setResetTime(long resetTime) {
+        this.resetTime = resetTime;
+    }
+
+    public boolean isForce() {
+        return force;
+    }
+
+    public void setForce(boolean force) {
+        this.force = force;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/SendTopicMessageRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/SendTopicMessageRequest.java
new file mode 100644
index 00000000..624b739e
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/SendTopicMessageRequest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+
+public class SendTopicMessageRequest {
+    private String topic;
+    private String key;
+    private String tag;
+    private String messageBody;
+    private boolean traceEnabled;
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getTag() {
+        return tag;
+    }
+
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+    public String getMessageBody() {
+        return messageBody;
+    }
+
+    public void setMessageBody(String messageBody) {
+        this.messageBody = messageBody;
+    }
+
+    public boolean isTraceEnabled() {
+        return traceEnabled;
+    }
+
+    public void setTraceEnabled(boolean traceEnabled) {
+        this.traceEnabled = traceEnabled;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeList.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeList.java
new file mode 100644
index 00000000..ec059079
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeList.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+import java.util.List;
+
+public class TopicTypeList {
+    private List<String> topicNameList;
+    private List<String> messageTypeList;
+
+    public List<String> getTopicNameList() {
+        return topicNameList;
+    }
+
+    public void setTopicNameList(List<String> topicNameList) {
+        this.topicNameList = topicNameList;
+    }
+
+    public List<String> getMessageTypeList() {
+        return messageTypeList;
+    }
+
+    public void setMessageTypeList(List<String> messageTypeList) {
+        this.messageTypeList = messageTypeList;
+    }
+
+    public TopicTypeList(List<String> topicNameList, List<String> messageTypeList) {
+        this.topicNameList = topicNameList;
+        this.messageTypeList = messageTypeList;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeMeta.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeMeta.java
new file mode 100644
index 00000000..22148962
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/TopicTypeMeta.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.request;
+
+public class TopicTypeMeta {
+    private String topicName;
+    private String messageType;
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public void setTopicName(String topicName) {
+        this.topicName = topicName;
+    }
+
+    public String getMessageType() {
+        return messageType;
+    }
+
+    public void setMessageType(String messageType) {
+        this.messageType = messageType;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/UserCreateRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserCreateRequest.java
new file mode 100644
index 00000000..20a76d53
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserCreateRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.request;
+
+
+public class UserCreateRequest {
+    private String clusterName;
+    private String brokerName;
+    private UserInfoParam userInfo;
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public UserInfoParam getUserInfo() {
+        return userInfo;
+    }
+
+    public void setUserInfo(UserInfoParam userInfo) {
+        this.userInfo = userInfo;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/UserInfoParam.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserInfoParam.java
new file mode 100644
index 00000000..f733edbd
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserInfoParam.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.request;
+
+
+public class UserInfoParam {
+    private String username;
+    private String password;
+    private String userStatus;
+    private String userType;
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUserStatus() {
+        return userStatus;
+    }
+
+    public void setUserStatus(String userStatus) {
+        this.userStatus = userStatus;
+    }
+
+    public String getUserType() {
+        return userType;
+    }
+
+    public void setUserType(String userType) {
+        this.userType = userType;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/request/UserUpdateRequest.java b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserUpdateRequest.java
new file mode 100644
index 00000000..65e8596d
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/request/UserUpdateRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.request;
+
+
+public class UserUpdateRequest {
+    private String clusterName;
+    private String brokerName;
+    private UserInfoParam userInfo;
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public UserInfoParam getUserInfo() {
+        return userInfo;
+    }
+
+    public void setUserInfo(UserInfoParam userInfo) {
+        this.userInfo = userInfo;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceGraph.java b/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceGraph.java
new file mode 100644
index 00000000..f1a0abcb
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceGraph.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.trace;
+
+
+import java.util.List;
+
+public class MessageTraceGraph {
+    private ProducerNode producerNode;
+    private List<SubscriptionNode> subscriptionNodeList;
+    private List<Object> messageTraceViews;
+
+    public ProducerNode getProducerNode() {
+        return producerNode;
+    }
+
+    public void setProducerNode(ProducerNode producerNode) {
+        this.producerNode = producerNode;
+    }
+
+    public List<SubscriptionNode> getSubscriptionNodeList() {
+        return subscriptionNodeList;
+    }
+
+    public void setSubscriptionNodeList(List<SubscriptionNode> subscriptionNodeList) {
+        this.subscriptionNodeList = subscriptionNodeList;
+    }
+
+    public List<Object> getMessageTraceViews() {
+        return messageTraceViews;
+    }
+
+    public void setMessageTraceViews(List<Object> messageTraceViews) {
+        this.messageTraceViews = messageTraceViews;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceStatusEnum.java b/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceStatusEnum.java
new file mode 100644
index 00000000..2827485e
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/trace/MessageTraceStatusEnum.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.trace;
+
+
+public enum MessageTraceStatusEnum {
+    SUCCESS("success"),
+    FAILED("failed"),
+    UNKNOWN("unknown");
+    private final String status;
+
+    MessageTraceStatusEnum(String status) {
+        this.status = status;
+    }
+    public String getStatus() {
+        return status;
+    }
+
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/trace/ProducerNode.java b/server/src/main/java/org/apache/rocketmq/studio/model/trace/ProducerNode.java
new file mode 100644
index 00000000..a3c40225
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/trace/ProducerNode.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.trace;
+
+
+import java.util.List;
+
+public class ProducerNode {
+    private String msgId;
+    private String tags;
+    private String keys;
+    private String offSetMsgId;
+    private String topic;
+    private String groupName;
+    private TraceNode traceNode;
+    private List<TraceNode> transactionNodeList;
+    public String getMsgId() {
+        return msgId;
+    }
+
+    public void setMsgId(String msgId) {
+        this.msgId = msgId;
+    }
+
+    public String getTags() {
+        return tags;
+    }
+
+    public void setTags(String tags) {
+        this.tags = tags;
+    }
+
+    public String getKeys() {
+        return keys;
+    }
+
+    public void setKeys(String keys) {
+        this.keys = keys;
+    }
+
+    public String getOffSetMsgId() {
+        return offSetMsgId;
+    }
+
+    public void setOffSetMsgId(String offSetMsgId) {
+        this.offSetMsgId = offSetMsgId;
+    }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(String groupName) {
+        this.groupName = groupName;
+    }
+
+    public TraceNode getTraceNode() {
+        return traceNode;
+    }
+
+    public void setTraceNode(TraceNode traceNode) {
+        this.traceNode = traceNode;
+    }
+
+    public List<TraceNode> getTransactionNodeList() {
+        return transactionNodeList;
+    }
+
+    public void setTransactionNodeList(List<TraceNode> transactionNodeList) {
+        this.transactionNodeList = transactionNodeList;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/trace/SubscriptionNode.java b/server/src/main/java/org/apache/rocketmq/studio/model/trace/SubscriptionNode.java
new file mode 100644
index 00000000..b0239a7f
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/trace/SubscriptionNode.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model.trace;
+
+
+import java.util.List;
+
+public class SubscriptionNode {
+    private String subscriptionGroup;
+    private List<TraceNode> consumeNodeList;
+    public String getSubscriptionGroup() {
+        return subscriptionGroup;
+    }
+
+    public void setSubscriptionGroup(String subscriptionGroup) {
+        this.subscriptionGroup = subscriptionGroup;
+    }
+
+    public List<TraceNode> getConsumeNodeList() {
+        return consumeNodeList;
+    }
+
+    public void setConsumeNodeList(List<TraceNode> consumeNodeList) {
+        this.consumeNodeList = consumeNodeList;
+    }
+
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/model/trace/TraceNode.java b/server/src/main/java/org/apache/rocketmq/studio/model/trace/TraceNode.java
new file mode 100644
index 00000000..921364ea
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/model/trace/TraceNode.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.studio.model.trace;
+
+
+public class TraceNode {
+    private String requestId;
+    private String storeHost;
+    private String clientHost;
+    private int costTime;
+    private long beginTimestamp;
+    private long endTimestamp;
+    private int retryTimes;
+    private String status;
+    private String transactionState;
+    private String transactionId;
+    private boolean fromTransactionCheck;
+    private String msgType;
+    public String getRequestId() {
+        return requestId;
+    }
+
+    public void setRequestId(String requestId) {
+        this.requestId = requestId;
+    }
+
+    public String getStoreHost() {
+        return storeHost;
+    }
+
+    public void setStoreHost(String storeHost) {
+        this.storeHost = storeHost;
+    }
+
+    public String getClientHost() {
+        return clientHost;
+    }
+
+    public void setClientHost(String clientHost) {
+        this.clientHost = clientHost;
+    }
+
+    public int getCostTime() {
+        return costTime;
+    }
+
+    public void setCostTime(int costTime) {
+        this.costTime = costTime;
+    }
+
+    public long getBeginTimestamp() {
+        return beginTimestamp;
+    }
+
+    public void setBeginTimestamp(long beginTimestamp) {
+        this.beginTimestamp = beginTimestamp;
+    }
+
+    public long getEndTimestamp() {
+        return endTimestamp;
+    }
+
+    public void setEndTimestamp(long endTimestamp) {
+        this.endTimestamp = endTimestamp;
+    }
+
+    public int getRetryTimes() {
+        return retryTimes;
+    }
+
+    public void setRetryTimes(int retryTimes) {
+        this.retryTimes = retryTimes;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getTransactionState() {
+        return transactionState;
+    }
+
+    public void setTransactionState(String transactionState) {
+        this.transactionState = transactionState;
+    }
+
+    public String getTransactionId() {
+        return transactionId;
+    }
+
+    public void setTransactionId(String transactionId) {
+        this.transactionId = transactionId;
+    }
+
+    public boolean isFromTransactionCheck() {
+        return fromTransactionCheck;
+    }
+
+    public void setFromTransactionCheck(boolean fromTransactionCheck) {
+        this.fromTransactionCheck = fromTransactionCheck;
+    }
+
+    public String getMsgType() {
+        return msgType;
+    }
+
+    public void setMsgType(String msgType) {
+        this.msgType = msgType;
+    }
+
+}
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ClientInstance.java b/src/main/java/org/apache/rocketmq/dashboard/model/ClientInstance.java
new file mode 100644
index 00000000..73d4ea02
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/ClientInstance.java
@@ -0,0 +1,373 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.List;
+
+public class ClientInstance {
+
+    private String clientId;
+
+    private String clientAddress;
+
+    private ClientType clientType;
+
+    /**
+     * Topics associated with this client (published topics for producers, subscribed topics for consumers)
+     */
+    private List<String> topics;
+
+    private String clientSubType;
+
+    private String language;
+
+    private String sdkVersion;
+
+    private ProtocolType protocolType;
+
+    private String endpoint;
+
+    private Date lastHeartbeatTime;
+
+    private boolean active;
+
+    private Date connectTime;
+
+    private String instanceName;
+
+    private String consumerGroup;
+
+    private String producerGroup;
+
+    private List<SubscriptionInfo> subscriptions;
+
+    private List<String> publishTopics;
+
+    private ConsumerProgress consumerProgress;
+
+    private String clientVersion;
+
+    private Boolean vipChannelEnabled;
+
+    private String telemetrySessionId;
+
+    private Boolean longConnectionActive;
+
+    private String settingsVersion;
+
+    private String authFailureReason;
+
+    private Boolean popEnabled;
+
+    private Integer pendingAckCount;
+
+    /**
+     * Subscription count for consumer clients
+     */
+    private Integer subscriptionCount;
+
+    /**
+     * Client status (ONLINE, OFFLINE, etc.)
+     */
+    private String status;
+
+    public String getDisplayName() {
+        if (instanceName != null && !instanceName.trim().isEmpty()) {
+            return instanceName;
+        }
+        return clientId;
+    }
+
+    public long getClientDelay() {
+        if (lastHeartbeatTime == null) {
+            return -1;
+        }
+        return (System.currentTimeMillis() - lastHeartbeatTime.getTime()) / 1000;
+    }
+
+    public boolean isGrpcClient() {
+        return ProtocolType.GRPC.equals(protocolType);
+    }
+
+    public boolean isRemotingClient() {
+        return ProtocolType.REMOTING.equals(protocolType);
+    }
+
+    public boolean isConsumer() {
+        return ClientType.CONSUMER.equals(clientType);
+    }
+
+    public boolean isProducer() {
+        return ClientType.PRODUCER.equals(clientType);
+    }
+
+    /**
+     * Get version string (alias for sdkVersion for backward compatibility)
+     */
+    public String getVersion() {
+        return sdkVersion;
+    }
+
+    /**
+     * Set version string (alias for sdkVersion for backward compatibility)
+     */
+    public void setVersion(String version) {
+        this.sdkVersion = version;
+    }
+
+    public enum ClientType {
+        PRODUCER, CONSUMER, PUSH_CONSUMER, PULL_CONSUMER, SIMPLE_CONSUMER
+    }
+
+    public enum ProtocolType {
+        REMOTING, GRPC
+    }
+
+        public static class ConsumerProgress {
+        private Long totalConsumed;
+        private Long totalBacklog;
+        private Double consumptionRate;
+        private Date lastConsumeTime;
+        private String consumptionMode; // PULL / PUSH / POP
+        private Boolean orderlyConsume;
+    }
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getClientAddress() {
+        return clientAddress;
+    }
+
+    public void setClientAddress(String clientAddress) {
+        this.clientAddress = clientAddress;
+    }
+
+    public ClientType getClientType() {
+        return clientType;
+    }
+
+    public void setClientType(ClientType clientType) {
+        this.clientType = clientType;
+    }
+
+    public List<String> getTopics() {
+        return topics;
+    }
+
+    public void setTopics(List<String> topics) {
+        this.topics = topics;
+    }
+
+    public String getClientSubType() {
+        return clientSubType;
+    }
+
+    public void setClientSubType(String clientSubType) {
+        this.clientSubType = clientSubType;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public String getSdkVersion() {
+        return sdkVersion;
+    }
+
+    public void setSdkVersion(String sdkVersion) {
+        this.sdkVersion = sdkVersion;
+    }
+
+    public ProtocolType getProtocolType() {
+        return protocolType;
+    }
+
+    public void setProtocolType(ProtocolType protocolType) {
+        this.protocolType = protocolType;
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public Date getLastHeartbeatTime() {
+        return lastHeartbeatTime;
+    }
+
+    public void setLastHeartbeatTime(Date lastHeartbeatTime) {
+        this.lastHeartbeatTime = lastHeartbeatTime;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+    public Date getConnectTime() {
+        return connectTime;
+    }
+
+    public void setConnectTime(Date connectTime) {
+        this.connectTime = connectTime;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    public void setInstanceName(String instanceName) {
+        this.instanceName = instanceName;
+    }
+
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
+    public String getProducerGroup() {
+        return producerGroup;
+    }
+
+    public void setProducerGroup(String producerGroup) {
+        this.producerGroup = producerGroup;
+    }
+
+    public List<SubscriptionInfo> getSubscriptions() {
+        return subscriptions;
+    }
+
+    public void setSubscriptions(List<SubscriptionInfo> subscriptions) {
+        this.subscriptions = subscriptions;
+    }
+
+    public List<String> getPublishTopics() {
+        return publishTopics;
+    }
+
+    public void setPublishTopics(List<String> publishTopics) {
+        this.publishTopics = publishTopics;
+    }
+
+    public ConsumerProgress getConsumerProgress() {
+        return consumerProgress;
+    }
+
+    public void setConsumerProgress(ConsumerProgress consumerProgress) {
+        this.consumerProgress = consumerProgress;
+    }
+
+    public String getClientVersion() {
+        return clientVersion;
+    }
+
+    public void setClientVersion(String clientVersion) {
+        this.clientVersion = clientVersion;
+    }
+
+    public Boolean getVipChannelEnabled() {
+        return vipChannelEnabled;
+    }
+
+    public void setVipChannelEnabled(Boolean vipChannelEnabled) {
+        this.vipChannelEnabled = vipChannelEnabled;
+    }
+
+    public String getTelemetrySessionId() {
+        return telemetrySessionId;
+    }
+
+    public void setTelemetrySessionId(String telemetrySessionId) {
+        this.telemetrySessionId = telemetrySessionId;
+    }
+
+    public Boolean getLongConnectionActive() {
+        return longConnectionActive;
+    }
+
+    public void setLongConnectionActive(Boolean longConnectionActive) {
+        this.longConnectionActive = longConnectionActive;
+    }
+
+    public String getSettingsVersion() {
+        return settingsVersion;
+    }
+
+    public void setSettingsVersion(String settingsVersion) {
+        this.settingsVersion = settingsVersion;
+    }
+
+    public String getAuthFailureReason() {
+        return authFailureReason;
+    }
+
+    public void setAuthFailureReason(String authFailureReason) {
+        this.authFailureReason = authFailureReason;
+    }
+
+    public Boolean getPopEnabled() {
+        return popEnabled;
+    }
+
+    public void setPopEnabled(Boolean popEnabled) {
+        this.popEnabled = popEnabled;
+    }
+
+    public Integer getPendingAckCount() {
+        return pendingAckCount;
+    }
+
+    public void setPendingAckCount(Integer pendingAckCount) {
+        this.pendingAckCount = pendingAckCount;
+    }
+
+    public Integer getSubscriptionCount() {
+        return subscriptionCount;
+    }
+
+    public void setSubscriptionCount(Integer subscriptionCount) {
+        this.subscriptionCount = subscriptionCount;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ClusterCapability.java b/src/main/java/org/apache/rocketmq/dashboard/model/ClusterCapability.java
new file mode 100644
index 00000000..750dd85c
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/ClusterCapability.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ClusterCapability {
+
+    private boolean liteTopicSupported;
+
+    private boolean popConsumeSupported;
+
+    private boolean aclV2Supported;
+
+    private boolean grpcClientSupported;
+
+    private boolean delayMessageSupported;
+
+    private boolean transactionMessageSupported;
+
+    private boolean fifoMessageSupported;
+
+    private String architectureVersion;
+
+    private String rocketmqVersion;
+
+    private Set<String> extendedCapabilities;
+
+    public Set<TopicType> getSupportedTopicTypes() {
+        Set<TopicType> supported = new HashSet<>();
+        supported.add(TopicType.NORMAL);
+
+        if (fifoMessageSupported) {
+            supported.add(TopicType.FIFO);
+        }
+        if (delayMessageSupported) {
+            supported.add(TopicType.DELAY);
+        }
+        if (transactionMessageSupported) {
+            supported.add(TopicType.TRANSACTION);
+        }
+        if (liteTopicSupported) {
+            supported.add(TopicType.LITE);
+        }
+
+        return supported;
+    }
+
+    public boolean hasCapability(String capability) {
+        if (extendedCapabilities != null) {
+            return extendedCapabilities.contains(capability);
+        }
+        return false;
+    }
+    public boolean isLiteTopicSupported() {
+        return liteTopicSupported;
+    }
+
+    public void setLiteTopicSupported(boolean liteTopicSupported) {
+        this.liteTopicSupported = liteTopicSupported;
+    }
+
+    public boolean isPopConsumeSupported() {
+        return popConsumeSupported;
+    }
+
+    public void setPopConsumeSupported(boolean popConsumeSupported) {
+        this.popConsumeSupported = popConsumeSupported;
+    }
+
+    public boolean isAclV2Supported() {
+        return aclV2Supported;
+    }
+
+    public void setAclV2Supported(boolean aclV2Supported) {
+        this.aclV2Supported = aclV2Supported;
+    }
+
+    public boolean isGrpcClientSupported() {
+        return grpcClientSupported;
+    }
+
+    public void setGrpcClientSupported(boolean grpcClientSupported) {
+        this.grpcClientSupported = grpcClientSupported;
+    }
+
+    public boolean isDelayMessageSupported() {
+        return delayMessageSupported;
+    }
+
+    public void setDelayMessageSupported(boolean delayMessageSupported) {
+        this.delayMessageSupported = delayMessageSupported;
+    }
+
+    public boolean isTransactionMessageSupported() {
+        return transactionMessageSupported;
+    }
+
+    public void setTransactionMessageSupported(boolean transactionMessageSupported) {
+        this.transactionMessageSupported = transactionMessageSupported;
+    }
+
+    public boolean isFifoMessageSupported() {
+        return fifoMessageSupported;
+    }
+
+    public void setFifoMessageSupported(boolean fifoMessageSupported) {
+        this.fifoMessageSupported = fifoMessageSupported;
+    }
+
+    public String getArchitectureVersion() {
+        return architectureVersion;
+    }
+
+    public void setArchitectureVersion(String architectureVersion) {
+        this.architectureVersion = architectureVersion;
+    }
+
+    public String getRocketmqVersion() {
+        return rocketmqVersion;
+    }
+
+    public void setRocketmqVersion(String rocketmqVersion) {
+        this.rocketmqVersion = rocketmqVersion;
+    }
+
+    public Set<String> getExtendedCapabilities() {
+        return extendedCapabilities;
+    }
+
+    public void setExtendedCapabilities(Set<String> extendedCapabilities) {
+        this.extendedCapabilities = extendedCapabilities;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ClusterTopology.java b/src/main/java/org/apache/rocketmq/dashboard/model/ClusterTopology.java
new file mode 100644
index 00000000..79257e44
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/ClusterTopology.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unified cluster topology model
+ * Hide differences between different cluster architectures
+ */
+public class ClusterTopology {
+
+    /**
+     * Cluster name
+     */
+    private String clusterName;
+
+    /**
+     * NameServer address list
+     */
+    private List<String> namesrvAddresses;
+
+    /**
+     * NameServer node list
+     */
+    private List<NodeInfo> namesrvNodes;
+
+    /**
+     * Broker node list
+     */
+    private List<NodeInfo> brokerNodes;
+
+    /**
+     * Proxy node list (5.0 architecture)
+     */
+    private List<NodeInfo> proxyNodes;
+
+    /**
+     * Node mapping table (for quick lookup)
+     */
+    private Map<String, NodeInfo> nodeMap;
+
+    public ClusterTopology() {
+        this.namesrvAddresses = new ArrayList<>();
+        this.namesrvNodes = new ArrayList<>();
+        this.brokerNodes = new ArrayList<>();
+        this.proxyNodes = new ArrayList<>();
+        this.nodeMap = new HashMap<>();
+    }
+
+    /**
+     * Add node
+     */
+    public void addNode(String nodeName, Long nodeId, String nodeAddress, String nodeType) {
+        NodeInfo node = new NodeInfo();
+        node.setNodeName(nodeName);
+        node.setNodeId(nodeId);
+        node.setNodeAddress(nodeAddress);
+        node.setNodeType(nodeType);
+        node.setClusterName(clusterName);
+
+        String key = nodeType + "-" + nodeName + "-" + nodeId;
+        nodeMap.put(key, node);
+
+        switch (nodeType) {
+            case "NAMESRV":
+                namesrvNodes.add(node);
+                break;
+            case "BROKER":
+                brokerNodes.add(node);
+                break;
+            case "PROXY":
+                proxyNodes.add(node);
+                break;
+        }
+    }
+
+    /**
+     * Get total node count
+     */
+    public int getTotalNodeCount() {
+        return namesrvNodes.size() + brokerNodes.size() + proxyNodes.size();
+    }
+
+    /**
+     * Get master broker count
+     */
+    public int getMasterBrokerCount() {
+        return (int) brokerNodes.stream()
+            .filter(node -> node.getNodeId() != null && node.getNodeId() == 0)
+            .count();
+    }
+
+    /**
+     * Get slave broker count
+     */
+    public int getSlaveBrokerCount() {
+        return (int) brokerNodes.stream()
+            .filter(node -> node.getNodeId() != null && node.getNodeId() > 0)
+            .count();
+    }
+
+        public static class NodeInfo {
+        private String nodeName;
+        private Long nodeId;
+        private String nodeAddress;
+        private String nodeType; // NAMESRV, BROKER, PROXY
+        private String clusterName;
+        private String status; // ONLINE, OFFLINE, UNKNOWN
+        private Long version;
+        private Map<String, Object> metadata;
+
+        public NodeInfo() {
+            this.metadata = new HashMap<>();
+            this.status = "UNKNOWN";
+        }
+
+        /**
+         * Check if node is master (Broker ID = 0)
+         */
+        public boolean isMaster() {
+            return nodeId != null && nodeId == 0;
+        }
+
+        /**
+         * Check if node is online
+         */
+        public boolean isOnline() {
+            return "ONLINE".equals(status);
+        }
+    }
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public List<String> getNamesrvAddresses() {
+        return namesrvAddresses;
+    }
+
+    public void setNamesrvAddresses(List<String> namesrvAddresses) {
+        this.namesrvAddresses = namesrvAddresses;
+    }
+
+    public List<NodeInfo> getNamesrvNodes() {
+        return namesrvNodes;
+    }
+
+    public void setNamesrvNodes(List<NodeInfo> namesrvNodes) {
+        this.namesrvNodes = namesrvNodes;
+    }
+
+    public List<NodeInfo> getBrokerNodes() {
+        return brokerNodes;
+    }
+
+    public void setBrokerNodes(List<NodeInfo> brokerNodes) {
+        this.brokerNodes = brokerNodes;
+    }
+
+    public List<NodeInfo> getProxyNodes() {
+        return proxyNodes;
+    }
+
+    public void setProxyNodes(List<NodeInfo> proxyNodes) {
+        this.proxyNodes = proxyNodes;
+    }
+
+    public Map<String, NodeInfo> getNodeMap() {
+        return nodeMap;
+    }
+
+    public void setNodeMap(Map<String, NodeInfo> nodeMap) {
+        this.nodeMap = nodeMap;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java
new file mode 100644
index 00000000..771bd410
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import com.google.common.collect.Sets;
+import org.apache.rocketmq.common.MQVersion;
+import org.apache.rocketmq.remoting.protocol.body.Connection;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+public class ConnectionInfo extends Connection {
+    private String versionDesc;
+
+    public static ConnectionInfo buildConnectionInfo(Connection connection) {
+        ConnectionInfo connectionInfo = new ConnectionInfo();
+        connectionInfo.setClientId(connection.getClientId());
+        connectionInfo.setClientAddr(connection.getClientAddr());
+        connectionInfo.setLanguage(connection.getLanguage());
+        connectionInfo.setVersion(connection.getVersion());
+        connectionInfo.setVersionDesc(MQVersion.getVersionDesc(connection.getVersion()));
+        return connectionInfo;
+    }
+
+    public static HashSet<Connection> buildConnectionInfoHashSet(Collection<Connection> connectionList) {
+        HashSet<Connection> connectionHashSet = Sets.newHashSet();
+        for (Connection connection : connectionList) {
+            connectionHashSet.add(buildConnectionInfo(connection));
+        }
+        return connectionHashSet;
+    }
+
+    public String getVersionDesc() {
+        return versionDesc;
+    }
+
+    public void setVersionDesc(String versionDesc) {
+        this.versionDesc = versionDesc;
+    }
+}
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupInfo.java
new file mode 100644
index 00000000..db0b7500
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupInfo.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+public class ConsumerGroupInfo {
+
+    private String consumerGroupName;
+
+    private String consumeMode;
+
+    private Boolean consumeMessageOrderly;
+
+    private Boolean consumeBroadcastEnable;
+
+    private Boolean consumeFromMinEnable;
+
+    private Integer retryQueueNums;
+
+    private Integer retryMaxTimes;
+
+    private Integer consumeTimeoutMinute;
+
+    private String clusterName;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String status;
+
+    private Set<String> subscribedTopics;
+
+    private Integer onlineClientCount;
+
+    private String liteBindTopic;
+
+    private Map<String, String> attributes;
+
+    private Integer groupSysFlag;
+
+    public String getDisplayName() {
+        return consumerGroupName;
+    }
+
+    public boolean isPopConsumer() {
+        return "POP".equalsIgnoreCase(consumeMode);
+    }
+
+    public boolean isOrderlyConsume() {
+        return Boolean.TRUE.equals(consumeMessageOrderly);
+    }
+
+    public boolean isBroadcastConsume() {
+        return Boolean.TRUE.equals(consumeBroadcastEnable);
+    }
+    public String getConsumerGroupName() {
+        return consumerGroupName;
+    }
+
+    public void setConsumerGroupName(String consumerGroupName) {
+        this.consumerGroupName = consumerGroupName;
+    }
+
+    public String getConsumeMode() {
+        return consumeMode;
+    }
+
+    public void setConsumeMode(String consumeMode) {
+        this.consumeMode = consumeMode;
+    }
+
+    public Boolean getConsumeMessageOrderly() {
+        return consumeMessageOrderly;
+    }
+
+    public void setConsumeMessageOrderly(Boolean consumeMessageOrderly) {
+        this.consumeMessageOrderly = consumeMessageOrderly;
+    }
+
+    public Boolean getConsumeBroadcastEnable() {
+        return consumeBroadcastEnable;
+    }
+
+    public void setConsumeBroadcastEnable(Boolean consumeBroadcastEnable) {
+        this.consumeBroadcastEnable = consumeBroadcastEnable;
+    }
+
+    public Boolean getConsumeFromMinEnable() {
+        return consumeFromMinEnable;
+    }
+
+    public void setConsumeFromMinEnable(Boolean consumeFromMinEnable) {
+        this.consumeFromMinEnable = consumeFromMinEnable;
+    }
+
+    public Integer getRetryQueueNums() {
+        return retryQueueNums;
+    }
+
+    public void setRetryQueueNums(Integer retryQueueNums) {
+        this.retryQueueNums = retryQueueNums;
+    }
+
+    public Integer getRetryMaxTimes() {
+        return retryMaxTimes;
+    }
+
+    public void setRetryMaxTimes(Integer retryMaxTimes) {
+        this.retryMaxTimes = retryMaxTimes;
+    }
+
+    public Integer getConsumeTimeoutMinute() {
+        return consumeTimeoutMinute;
+    }
+
+    public void setConsumeTimeoutMinute(Integer consumeTimeoutMinute) {
+        this.consumeTimeoutMinute = consumeTimeoutMinute;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Set<String> getSubscribedTopics() {
+        return subscribedTopics;
+    }
+
+    public void setSubscribedTopics(Set<String> subscribedTopics) {
+        this.subscribedTopics = subscribedTopics;
+    }
+
+    public Integer getOnlineClientCount() {
+        return onlineClientCount;
+    }
+
+    public void setOnlineClientCount(Integer onlineClientCount) {
+        this.onlineClientCount = onlineClientCount;
+    }
+
+    public String getLiteBindTopic() {
+        return liteBindTopic;
+    }
+
+    public void setLiteBindTopic(String liteBindTopic) {
+        this.liteBindTopic = liteBindTopic;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public Integer getGroupSysFlag() {
+        return groupSysFlag;
+    }
+
+    public void setGroupSysFlag(Integer groupSysFlag) {
+        this.groupSysFlag = groupSysFlag;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java
new file mode 100644
index 00000000..7e9d4179
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
+import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
+
+import java.util.Date;
+import java.util.List;
+
+public class GroupConsumeInfo implements Comparable<GroupConsumeInfo> {
+    private String group;
+    private String version;
+    private int count;
+    private ConsumeType consumeType;
+    private MessageModel messageModel;
+    private List<String> address;
+    private int consumeTps;
+    private long diffTotal = -1;
+    private String subGroupType = "NORMAL";
+    private Date updateTime;
+
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public ConsumeType getConsumeType() {
+        return consumeType;
+    }
+
+    public void setConsumeType(ConsumeType consumeType) {
+        this.consumeType = consumeType;
+    }
+
+    public MessageModel getMessageModel() {
+        return messageModel;
+    }
+
+    public void setMessageModel(MessageModel messageModel) {
+        this.messageModel = messageModel;
+    }
+
+    public long getDiffTotal() {
+        return diffTotal;
+    }
+
+    public void setDiffTotal(long diffTotal) {
+        this.diffTotal = diffTotal;
+    }
+
+    public List<String> getAddress() {
+        return address;
+    }
+
+    public void setAddress(List<String> address) {
+        this.address = address;
+    }
+
+    public String getSubGroupType() {
+        return subGroupType;
+    }
+
+    public void setSubGroupType(String subGroupType) {
+        this.subGroupType = subGroupType;
+    }
+
+    @Override
+    public int compareTo(GroupConsumeInfo o) {
+        if (this.count != o.count) {
+            return Integer.compare(o.count, this.count);
+        }
+        return Long.compare(o.diffTotal, this.diffTotal);
+    }
+
+    public int getConsumeTps() {
+        return consumeTps;
+    }
+
+    public void setConsumeTps(int consumeTps) {
+        this.consumeTps = consumeTps;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+}
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/SubscriptionInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/SubscriptionInfo.java
new file mode 100644
index 00000000..ccc3bd59
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/SubscriptionInfo.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Map;
+
+public class SubscriptionInfo {
+
+    private String topic;
+
+    private String consumerGroup;
+
+    private String subExpression;
+
+    private String subscriptionType;
+
+    private Long version;
+
+    private Boolean broadcast;
+
+    private String consumeFromWhere;
+
+    private Long consumeProgress;
+
+    private Long backlogCount;
+
+    private Map<String, Object> attributes;
+
+    public boolean isTagSubscription() {
+        return "TAG".equals(subscriptionType);
+    }
+
+    public boolean isSQL92Subscription() {
+        return "SQL92".equals(subscriptionType);
+    }
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
+    public String getSubExpression() {
+        return subExpression;
+    }
+
+    public void setSubExpression(String subExpression) {
+        this.subExpression = subExpression;
+    }
+
+    public String getSubscriptionType() {
+        return subscriptionType;
+    }
+
+    public void setSubscriptionType(String subscriptionType) {
+        this.subscriptionType = subscriptionType;
+    }
+
+    public Long getVersion() {
+        return version;
+    }
+
+    public void setVersion(Long version) {
+        this.version = version;
+    }
+
+    public Boolean getBroadcast() {
+        return broadcast;
+    }
+
+    public void setBroadcast(Boolean broadcast) {
+        this.broadcast = broadcast;
+    }
+
+    public String getConsumeFromWhere() {
+        return consumeFromWhere;
+    }
+
+    public void setConsumeFromWhere(String consumeFromWhere) {
+        this.consumeFromWhere = consumeFromWhere;
+    }
+
+    public Long getConsumeProgress() {
+        return consumeProgress;
+    }
+
+    public void setConsumeProgress(Long consumeProgress) {
+        this.consumeProgress = consumeProgress;
+    }
+
+    public Long getBacklogCount() {
+        return backlogCount;
+    }
+
+    public void setBacklogCount(Long backlogCount) {
+        this.backlogCount = backlogCount;
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, Object> attributes) {
+        this.attributes = attributes;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java
new file mode 100644
index 00000000..cf22f132
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+public class TopicConsumerInfo {
+    private String topic;
+    private long diffTotal;
+    private long lastTimestamp;
+    private List<QueueStatInfo> queueStatInfoList = Lists.newArrayList();
+
+    public TopicConsumerInfo(String topic) {
+        this.topic = topic;
+    }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public long getDiffTotal() {
+        return diffTotal;
+    }
+
+    public void setDiffTotal(long diffTotal) {
+        this.diffTotal = diffTotal;
+    }
+
+    public List<QueueStatInfo> getQueueStatInfoList() {
+        return queueStatInfoList;
+    }
+
+    public long getLastTimestamp() {
+        return lastTimestamp;
+    }
+
+    public void appendQueueStatInfo(QueueStatInfo queueStatInfo) {
+        queueStatInfoList.add(queueStatInfo);
+        diffTotal = diffTotal + (queueStatInfo.getBrokerOffset() - queueStatInfo.getConsumerOffset());
+        lastTimestamp = Math.max(lastTimestamp, queueStatInfo.getLastTimestamp());
+    }
+}
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/TopicInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/TopicInfo.java
new file mode 100644
index 00000000..bf5082cb
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/TopicInfo.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+import java.util.Date;
+import java.util.Map;
+
+public class TopicInfo {
+
+    private String topicName;
+
+    private TopicType topicType;
+
+    private Integer readQueueNums;
+
+    private Integer writeQueueNums;
+
+    private Integer perm;
+
+    private Boolean orderTopic;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String topicStatus;
+
+    private String clusterName;
+
+    private Map<String, String> attributes;
+
+    private Long fifoTimeoutSeconds;
+
+    private Long liteTopicTTL;
+
+    /**
+     * LiteTopic - Session ID
+     */
+    private String sessionId;
+
+    private String autoCreatePattern;
+
+    private String delayLevel;
+
+    private String transactionServerAddr;
+
+    private Long transactionTimeoutSeconds;
+
+    public String getDisplayName() {
+        return topicName;
+    }
+
+    public boolean isLiteTopic() {
+        return TopicType.LITE.equals(topicType);
+    }
+
+    public boolean isOrderTopic() {
+        return Boolean.TRUE.equals(orderTopic) || TopicType.FIFO.equals(topicType);
+    }
+
+    public boolean isDelayTopic() {
+        return TopicType.DELAY.equals(topicType);
+    }
+
+    public boolean isTransactionTopic() {
+        return TopicType.TRANSACTION.equals(topicType);
+    }
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public void setTopicName(String topicName) {
+        this.topicName = topicName;
+    }
+
+    public TopicType getTopicType() {
+        return topicType;
+    }
+
+    public void setTopicType(TopicType topicType) {
+        this.topicType = topicType;
+    }
+
+    public Integer getReadQueueNums() {
+        return readQueueNums;
+    }
+
+    public void setReadQueueNums(Integer readQueueNums) {
+        this.readQueueNums = readQueueNums;
+    }
+
+    public Integer getWriteQueueNums() {
+        return writeQueueNums;
+    }
+
+    public void setWriteQueueNums(Integer writeQueueNums) {
+        this.writeQueueNums = writeQueueNums;
+    }
+
+    public Integer getPerm() {
+        return perm;
+    }
+
+    public void setPerm(Integer perm) {
+        this.perm = perm;
+    }
+
+    public Boolean getOrderTopic() {
+        return orderTopic;
+    }
+
+    public void setOrderTopic(Boolean orderTopic) {
+        this.orderTopic = orderTopic;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getTopicStatus() {
+        return topicStatus;
+    }
+
+    public void setTopicStatus(String topicStatus) {
+        this.topicStatus = topicStatus;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public Long getFifoTimeoutSeconds() {
+        return fifoTimeoutSeconds;
+    }
+
+    public void setFifoTimeoutSeconds(Long fifoTimeoutSeconds) {
+        this.fifoTimeoutSeconds = fifoTimeoutSeconds;
+    }
+
+    public Long getLiteTopicTTL() {
+        return liteTopicTTL;
+    }
+
+    public void setLiteTopicTTL(Long liteTopicTTL) {
+        this.liteTopicTTL = liteTopicTTL;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    public String getAutoCreatePattern() {
+        return autoCreatePattern;
+    }
+
+    public void setAutoCreatePattern(String autoCreatePattern) {
+        this.autoCreatePattern = autoCreatePattern;
+    }
+
+    public String getDelayLevel() {
+        return delayLevel;
+    }
+
+    public void setDelayLevel(String delayLevel) {
+        this.delayLevel = delayLevel;
+    }
+
+    public String getTransactionServerAddr() {
+        return transactionServerAddr;
+    }
+
+    public void setTransactionServerAddr(String transactionServerAddr) {
+        this.transactionServerAddr = transactionServerAddr;
+    }
+
+    public Long getTransactionTimeoutSeconds() {
+        return transactionTimeoutSeconds;
+    }
+
+    public void setTransactionTimeoutSeconds(Long transactionTimeoutSeconds) {
+        this.transactionTimeoutSeconds = transactionTimeoutSeconds;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/TopicType.java b/src/main/java/org/apache/rocketmq/dashboard/model/TopicType.java
new file mode 100644
index 00000000..9e12fb24
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/TopicType.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.model;
+
+public enum TopicType {
+
+    NORMAL("NORMAL", "Normal message", false),
+
+    FIFO("FIFO", "FIFO ordered message", true),
+
+    DELAY("DELAY", "Delay message", true),
+
+    TRANSACTION("TRANSACTION", "Transaction message", true),
+
+    LITE("LITE", "LiteTopic", true);
+
+    private final String value;
+    private final String description;
+    private final boolean requiresSpecialConfig;
+
+    TopicType(String value, String description, boolean requiresSpecialConfig) {
+        this.value = value;
+        this.description = description;
+        this.requiresSpecialConfig = requiresSpecialConfig;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public boolean isRequiresSpecialConfig() {
+        return requiresSpecialConfig;
+    }
+
+    public boolean isV5Specific() {
+        return this == LITE;
+    }
+
+    public boolean isNotSupportedInV4() {
+        return this == LITE;
+    }
+
+    public static TopicType fromValue(String value) {
+        for (TopicType type : values()) {
+            if (type.getValue().equals(value)) {
+                return type;
+            }
+        }
+        throw new IllegalArgumentException("Unknown topic type: " + value);
+    }
+}
\ No newline at end of file