feat(GOV-008): 采用 RocketMQ 消息队列架构

This commit is contained in:
2026-08-13 14:22:50 +08:00
parent 3cc00de8f6
commit f5994ad77e
34 changed files with 463 additions and 43 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **a-share-analysis** (879 symbols, 1062 relationships, 23 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. This project is indexed by GitNexus as **a-share-analysis** (958 symbols, 1141 relationships, 23 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
+6
View File
@@ -2,6 +2,12 @@
本项目采用语义化版本。功能级变化请从 `docs/features/` 按功能编号追溯。 本项目采用语义化版本。功能级变化请从 `docs/features/` 按功能编号追溯。
## 0.0.7 - 2026-08-13
- 将项目唯一消息队列技术基线由 RabbitMQ 调整为 RocketMQ 5.x。
- 建立 MessageBusAdapter、Topic/Group、重试、死信和顺序消息约束。
- 更新 Scheduler/Worker 环境模板和双部署文档,并记录本机 NameServer 分层连接验证。
## 0.0.6 - 2026-08-13 ## 0.0.6 - 2026-08-13
- 建立上一发布版本开发数据库 Schema 基线生成、规范化和自动校验工具。 - 建立上一发布版本开发数据库 Schema 基线生成、规范化和自动校验工具。
+1 -1
View File
@@ -10,7 +10,7 @@ A read-only A-share market overview and analysis tool.
- `web/`: Vue 3 market terminal frontend. - `web/`: Vue 3 market terminal frontend.
- `server/`: NestJS API, scheduler, and workers. Added in a later module. - `server/`: NestJS API, scheduler, and workers. Added in a later module.
- `deploy/`: MySQL, Redis, and RabbitMQ deployment configuration. Added with the backend module. - `deploy/`: MySQL 5.7, Redis, and RocketMQ 5.x deployment configuration. Added with the backend module.
- `docs/`: Product and implementation specifications. - `docs/`: Product and implementation specifications.
The first implementation slice is the market overview screen in `web/`. The first implementation slice is the market overview screen in `web/`.
+1 -1
View File
@@ -1 +1 @@
0.0.6 0.0.7
+6 -5
View File
@@ -11,8 +11,9 @@ REDIS_PASSWORD=
REDIS_DATABASE=1 REDIS_DATABASE=1
REDIS_TLS=false REDIS_TLS=false
# 第一阶段关闭 RabbitMQ,任务通过服务内 Adapter 调用。 # 第一阶段关闭 RocketMQ,任务通过服务内 MessageBusAdapter 调用。
RABBITMQ_ENABLED=false ROCKETMQ_ENABLED=false
RABBITMQ_URL= ROCKETMQ_NAMESRV_ADDR=127.0.0.1:9876
RABBITMQ_EXCHANGE=a-share-analysis ROCKETMQ_ENDPOINT=127.0.0.1:8081
RABBITMQ_PREFETCH=10 ROCKETMQ_TOPIC_PREFIX=a-share-analysis
ROCKETMQ_SEND_TIMEOUT_MS=3000
+6 -4
View File
@@ -22,10 +22,12 @@ MYSQL_PASSWORD=
MYSQL_CHARSET=utf8mb4 MYSQL_CHARSET=utf8mb4
MYSQL_COLLATION=utf8mb4_general_ci MYSQL_COLLATION=utf8mb4_general_ci
RABBITMQ_ENABLED=false ROCKETMQ_ENABLED=false
RABBITMQ_URL= ROCKETMQ_NAMESRV_ADDR=127.0.0.1:9876
RABBITMQ_EXCHANGE=a-share-analysis ROCKETMQ_ENDPOINT=127.0.0.1:8081
RABBITMQ_PREFETCH=10 ROCKETMQ_TOPIC_PREFIX=a-share-analysis
ROCKETMQ_CONSUMER_GROUP=a-share-analysis-worker
ROCKETMQ_CONSUMER_CONCURRENCY=4
AI_ENABLED=false AI_ENABLED=false
AI_BASE_URL= AI_BASE_URL=
+5 -4
View File
@@ -10,7 +10,8 @@ REDIS_PASSWORD=
REDIS_DATABASE=1 REDIS_DATABASE=1
REDIS_TLS=false REDIS_TLS=false
RABBITMQ_ENABLED=false ROCKETMQ_ENABLED=false
RABBITMQ_URL= ROCKETMQ_NAMESRV_ADDR=
RABBITMQ_EXCHANGE=a-share-analysis ROCKETMQ_ENDPOINT=
RABBITMQ_PREFETCH=10 ROCKETMQ_TOPIC_PREFIX=a-share-analysis
ROCKETMQ_SEND_TIMEOUT_MS=3000
+6 -4
View File
@@ -22,10 +22,12 @@ MYSQL_PASSWORD=
MYSQL_CHARSET=utf8mb4 MYSQL_CHARSET=utf8mb4
MYSQL_COLLATION=utf8mb4_general_ci MYSQL_COLLATION=utf8mb4_general_ci
RABBITMQ_ENABLED=false ROCKETMQ_ENABLED=false
RABBITMQ_URL= ROCKETMQ_NAMESRV_ADDR=
RABBITMQ_EXCHANGE=a-share-analysis ROCKETMQ_ENDPOINT=
RABBITMQ_PREFETCH=10 ROCKETMQ_TOPIC_PREFIX=a-share-analysis
ROCKETMQ_CONSUMER_GROUP=a-share-analysis-worker
ROCKETMQ_CONSUMER_CONCURRENCY=4
AI_ENABLED=false AI_ENABLED=false
AI_BASE_URL= AI_BASE_URL=
+31
View File
@@ -0,0 +1,31 @@
# ADR-0003:采用 RocketMQ 5.x 作为唯一消息队列
- 状态:accepted
- 日期:2026-08-13
- 关联功能:GOV-008
## 背景
项目此前在架构和环境模板中约定 RabbitMQ,但尚未实现消息客户端、Topic 或业务消费流程。产品需要分钟级盘中分析、定时采集、指标计算、AI 分析和失败重试,决定在实现前统一切换到 RocketMQ。
## 决策
- 项目唯一消息队列为 RocketMQ 5.x,不维护 RabbitMQ 双栈。
- Scheduler 和 Worker 只依赖项目级 `MessageBusAdapter` 与版本化事件契约。
- Node.js 客户端优先通过 RocketMQ 5.x Proxy 接入;若改用直接 NameServer/Broker 客户端,必须通过兼容性和可靠性验证。
- Topic、Tag、Producer Group、Consumer Group、顺序键、重试与死信语义必须在功能设计中明确。
- 本地和生产基础设施独立于应用发布;NameServer、Broker、Proxy 不随应用版本自动创建或删除。
## 原因
RocketMQ 对定时/延迟任务、重试、死信、顺序消息和后续任务规模扩展更符合预期。Adapter 边界用于控制 Node 客户端成熟度和接入协议变化带来的风险。
## 代价
- 本地和生产运维复杂度高于单节点 RabbitMQ,需要区分 NameServer、Broker 和 Proxy。
- Node.js 生态需要在 Server 脚手架阶段单独验证,不能仅凭 NameServer 端口可达决定客户端可用。
- 所有消费者必须按至少一次投递设计幂等和受控重放。
## 验证
GOV-008 只验证配置、文档契约和本机 NameServer 状态。真实 Topic 生产/消费必须在 Broker/Proxy 可用且 Server 客户端选定后补充契约测试。
+7 -5
View File
@@ -7,8 +7,8 @@
```text ```text
web/ Vue 3 前端 web/ Vue 3 前端
server/ NestJS API、Scheduler 与 Worker server/ NestJS API、Scheduler 与 Worker
contracts/ HTTP 与 RabbitMQ 版本化契约 contracts/ HTTP 与 RocketMQ 版本化契约
deploy/ MySQL、Redis、RabbitMQ 和运行配置 deploy/ MySQL、Redis、RocketMQ 5.x 和运行配置
docs/ 规范、架构、ADR 与功能档案 docs/ 规范、架构、ADR 与功能档案
tools/ 可重复执行的治理和开发工具 tools/ 可重复执行的治理和开发工具
``` ```
@@ -20,7 +20,8 @@ tools/ 可重复执行的治理和开发工具
```text ```text
免费数据源 免费数据源
→ Scheduler 定时投递 → Scheduler 定时投递
→ RabbitMQ → MessageBusAdapter
→ RocketMQ 5.x
→ 行情采集 Worker → 行情采集 Worker
→ 指标计算 Worker → 指标计算 Worker
→ 规则 / AI / 混合分析 → 规则 / AI / 混合分析
@@ -30,7 +31,8 @@ tools/ 可重复执行的治理和开发工具
``` ```
- Scheduler只负责触发时间。 - Scheduler只负责触发时间。
- RabbitMQ只负责任务传递、确认、重试和死信。 - MessageBusAdapter 隔离业务与 RocketMQ 客户端,禁止业务模块直接依赖 SDK。
- RocketMQ 只负责任务传递、确认、重试、延迟消息和死信;重复投递由 Worker 幂等处理。
- Worker负责采集、标准化、计算和分析。 - Worker负责采集、标准化、计算和分析。
- MySQL保存可追溯事实与历史报告。 - MySQL保存可追溯事实与历史报告。
- Redis保存最新快照、缓存、锁和限流状态。 - Redis保存最新快照、缓存、锁和限流状态。
@@ -52,7 +54,7 @@ tools/ 可重复执行的治理和开发工具
- 应用运行环境只定义 `development` 与 `production`;生产环境禁止模拟数据、热更新、调试端口和开发默认值回退。 - 应用运行环境只定义 `development` 与 `production`;生产环境禁止模拟数据、热更新、调试端口和开发默认值回退。
- API、Scheduler、Worker、Web 分别拥有开发和生产模板;同一进程跨环境使用相同变量名,通过启动 Schema 校验条件必填项。 - API、Scheduler、Worker、Web 分别拥有开发和生产模板;同一进程跨环境使用相同变量名,通过启动 Schema 校验条件必填项。
- 本地开发使用 Docker Compose 只启动 Redis,应用进程从源码运行;第一阶段 MySQL 与 RabbitMQ 保持关闭。 - 本地开发使用 Docker Compose 只启动 Redis,应用进程从源码运行;第一阶段 MySQL 与 RocketMQ 保持关闭。需要联调时可以连接本机独立运行的 RocketMQ 5.x。
- 生产同时支持 Docker Compose 和 Ubuntu 22.04/24.04 LTS x86_64 源码部署,两者共用配置契约和进程边界。 - 生产同时支持 Docker Compose 和 Ubuntu 22.04/24.04 LTS x86_64 源码部署,两者共用配置契约和进程边界。
- 源码部署由管理员预装 Node.js 22 LTS、pnpm 10、Nginx 和基础设施;项目只验证,不安装或修改系统软件源。 - 源码部署由管理员预装 Node.js 22 LTS、pnpm 10、Nginx 和基础设施;项目只验证,不安装或修改系统软件源。
- 源码部署使用不可变发布目录、`a-share` 用户和 systemd 独立进程配置;生产 env 存放在 `/etc/a-share-analysis/`,不随代码版本切换覆盖。 - 源码部署使用不可变发布目录、`a-share` 用户和 systemd 独立进程配置;生产 env 存放在 `/etc/a-share-analysis/`,不随代码版本切换覆盖。
+3 -2
View File
@@ -7,11 +7,12 @@
- 从 `deploy/env/production/` 准备四份真实配置,存放在服务器 `/etc/a-share-analysis/` 或其他受控目录。 - 从 `deploy/env/production/` 准备四份真实配置,存放在服务器 `/etc/a-share-analysis/` 或其他受控目录。
- Compose 通过外部路径加载,不在 YAML、镜像和 Git 中保存秘密。 - Compose 通过外部路径加载,不在 YAML、镜像和 Git 中保存秘密。
- 镜像引用必须包含版本或内容摘要,禁止浮动 `latest`。 - 镜像引用必须包含版本或内容摘要,禁止浮动 `latest`。
- 第一阶段只依赖 Redis;MySQL 和 RabbitMQ 功能开关保持关闭。 - 第一阶段只依赖 Redis;MySQL 和 RocketMQ 5.x 功能开关保持关闭。
- 启用 RocketMQ 时优先连接受运维管理的外部集群或独立基础设施 Compose,不把 NameServer、Broker、Proxy 混入应用发布生命周期。
## 发布与回退 ## 发布与回退
部署前验证镜像对应已推送提交或标签、环境 Schema、健康检查和数据目录。升级只修改不可变镜像引用;失败时恢复上一完整镜像集合和未改变的外部配置。禁止删除 Redis、MySQL 或 RabbitMQ 数据目录作为应用回退手段。 部署前验证镜像对应已推送提交或标签、环境 Schema、健康检查和数据目录。升级只修改不可变镜像引用;失败时恢复上一完整镜像集合和未改变的外部配置。禁止删除 Redis、MySQL 或 RocketMQ CommitLog 数据目录作为应用回退手段。
## 验证 ## 验证
+7 -1
View File
@@ -1,6 +1,6 @@
# 本地开发环境 # 本地开发环境
本地开发使用 Docker Compose 只启动 Redis,Web、API、Scheduler 和 Worker 从源码运行并使用开发环境模板。第一阶段不启动 MySQL 和 RabbitMQ。 本地开发使用 Docker Compose 只启动 Redis,Web、API、Scheduler 和 Worker 从源码运行并使用开发环境模板。第一阶段不自动启动 MySQL 和 RocketMQ 5.x。
## 配置 ## 配置
@@ -16,6 +16,12 @@ docker compose -f deploy/compose/development.yml down
Redis 只绑定 `127.0.0.1:6379`,数据写入仓库忽略的 `.local/redis/`。不得把该 Compose 文件用于生产。 Redis 只绑定 `127.0.0.1:6379`,数据写入仓库忽略的 `.local/redis/`。不得把该 Compose 文件用于生产。
## RocketMQ 联调
RocketMQ 由开发者在项目 Compose 外独立启动。应用启用前至少需要 NameServer、Broker;采用推荐的 Node.js Proxy 接入时还需要 Proxy。默认端口约定:NameServer `9876`、Broker `10911`、Proxy gRPC `8081`。
`nc -vz 127.0.0.1 9876` 只能证明 NameServer TCP 可达,不能证明 Topic 路由、Broker 注册或消息收发可用。完整联调必须继续检查 Broker 列表,并执行一次测试 Topic 的生产与消费。开发模板默认 `ROCKETMQ_ENABLED=false`;启用后必须提供可用的 `ROCKETMQ_ENDPOINT`,不得把生产地址写入本地配置或仓库。
## 应用状态 ## 应用状态
Web 和 Server 尚未完成脚手架,因此本版本只交付配置契约和 Redis 开发环境。后续脚手架必须使用 Node.js 22 LTS、pnpm 10 和这些模板,不能在本文档中假装应用已经可启动。 Web 和 Server 尚未完成脚手架,因此本版本只交付配置契约和 Redis 开发环境。后续脚手架必须使用 Node.js 22 LTS、pnpm 10 和这些模板,不能在本文档中假装应用已经可启动。
+3 -1
View File
@@ -1,6 +1,6 @@
# Ubuntu 源码生产部署 # Ubuntu 源码生产部署
正式支持 Ubuntu 22.04 LTS 与 Ubuntu 24.04 LTS 的 x86_64。服务器管理员预先提供 Node.js 22 LTS、pnpm 10、Nginx,以及 MySQL 5.7、Redis、RabbitMQ 或相应外部服务;项目不负责安装或升级这些软件,也不修改系统软件源。 正式支持 Ubuntu 22.04 LTS 与 Ubuntu 24.04 LTS 的 x86_64。服务器管理员预先提供 Node.js 22 LTS、pnpm 10、Nginx,以及 MySQL 5.7、Redis、RocketMQ 5.x 或相应外部服务;项目不负责安装或升级这些软件,也不修改系统软件源。
## 前置验证 ## 前置验证
@@ -33,3 +33,5 @@ nginx -v
在新的不可变发布目录完成安装、构建和配置校验后,原子切换 `current`,依次重启 API、Scheduler、Worker 并检查 `systemctl status`、`journalctl` 和 HTTP 健康端点。失败时切回上一完整目录并重启服务;生产配置不随版本切换覆盖。 在新的不可变发布目录完成安装、构建和配置校验后,原子切换 `current`,依次重启 API、Scheduler、Worker 并检查 `systemctl status`、`journalctl` 和 HTTP 健康端点。失败时切回上一完整目录并重启服务;生产配置不随版本切换覆盖。
项目部署流程禁止连接或读取生产数据库做开发验证。数据库备份属于独立运维职责,应用回退不得删除或覆盖数据。 项目部署流程禁止连接或读取生产数据库做开发验证。数据库备份属于独立运维职责,应用回退不得删除或覆盖数据。
RocketMQ 由运维独立维护 NameServer、Broker 和需要时的 Proxy。应用发布只读取受控的 RocketMQ Endpoint、Topic 前缀与 Group 配置,不负责创建或升级集群,也不得把 Broker 数据目录放进应用发布目录。
@@ -23,7 +23,7 @@
## 非目标 ## 非目标
- 不做自选股、持仓、笔记、交易计划、社区、荐股、跟单和公开组合。 - 不做自选股、持仓、笔记、交易计划、社区、荐股、跟单和公开组合。
- 本切片不接正式行情源、RabbitMQ、MySQL、Redis和付费 AI。 - 本切片不接正式行情源、RocketMQ、MySQL、Redis和付费 AI。
- 本切片不实现用户、邀请、会员、订阅和支付。 - 本切片不实现用户、邀请、会员、订阅和支付。
- ETF监控、两融数据、大盘云图和实时消息仍是独立一级模块。 - ETF监控、两融数据、大盘云图和实时消息仍是独立一级模块。
@@ -0,0 +1,45 @@
# RocketMQ 消息队列架构调整设计
- 功能编号:GOV-008
- 设计版本:0.1
- 状态:approved
- 日期:2026-08-13
## 架构
```text
Scheduler
-> MessageBusAdapter.publish()
-> RocketMQ 5.x Proxy 或受支持客户端
-> NameServer / Broker
-> MessageBusAdapter.consume()
-> Worker
-> Redis 当前状态 + MySQL 事实记录
```
`MessageBusAdapter` 是稳定边界。业务消息、Topic 和 Group 不引用 RocketMQ SDK 类型,未来客户端实现变化不要求重写调度与分析逻辑。项目只维护 RocketMQ 一套实现,不同时维护 RabbitMQ 兼容层。
## 接入模式
推荐 Node.js 通过 RocketMQ 5.x Proxy 接入,以降低对旧 Remoting 客户端实现的依赖。若后续选定的 Node 客户端直接连接 NameServer/Broker,必须先验证客户端维护状态、RocketMQ 5.3.2 兼容性、重试语义和优雅关闭,再通过 Adapter 接入。
`ROCKETMQ_NAMESRV_ADDR` 用于基础设施发现和诊断,`ROCKETMQ_ENDPOINT` 是应用客户端实际接入地址。两者不得混为同一项:只有 NameServer 端口可达时,不能完成生产或消费。
## Topic 与 Group
- Topic 前缀固定为 `a-share-analysis`,实际 Topic 按业务域追加,例如行情采集或指标计算。
- Tag 使用稳定事件类型,不用临时任务 ID。
- RocketMQ 5.x Proxy Producer 不配置 Producer Group;Worker 按独立消费语义使用 Consumer Group。
- 环境隔离通过受控前缀或独立集群完成,禁止开发消费者读取生产 Topic。
## 可靠性
RocketMQ 提供至少一次投递语义,Worker 必须幂等。消息处理完成并形成可验证结果后才能确认;可重试错误返回重试,永久错误记录原因后进入死信。顺序消息只在明确消息组键内保证,不假设跨证券或跨交易日全局顺序。
最大投递次数是 Consumer Group 的服务端重试策略,由 RocketMQ 运维配置和发布检查负责。应用客户端只声明 Consumer Group、并发与处理结果,不能用一个不会被官方 Node SDK读取的 env 字段假装控制死信边界。
## 本地状态
2026-08-13 首次检查到 `apache/rocketmq:5.3.2` 的 `rmqnamesrv` 容器曾运行并映射 `9876:9876`,日志为 NameServer boot success;随后串行复核发现该容器已以 137 退出且未配置自动重启,`OOMKilled=false`。
宿主机 `9876` 实际由 `/usr/local/rocketmq` 的原生 NameServer 监听,同时存在 `mqbroker --enable-proxy`。只读 `mqadmin` 验证结果:`broker-a` 已注册,版本 `V5_3_2`、`brokerActive=true`,Broker 地址 `192.168.2.37:10911`;本机 `10911`、Proxy `8081` 和 NameServer `9876` 均可达,Topic 列表可读取。当前可确认 RocketMQ 基础设施和管理链路连通,但尚未通过项目 Node 客户端执行消息收发。
@@ -0,0 +1,20 @@
# GitNexus 开发后基线
## 状态
```text
Repository: /Users/citrons/Documents/Codex/2026-08-12/wo-x/a-share-analysis
Indexed: 8/13/2026, 2:12:02 PM
Indexed commit: 3cc00de
Current commit: 3cc00de
Status: ✅ up-to-date
```
## 图谱规模
- 949 个节点。
- 1132 条关系。
- 13 个聚类。
- 23 条流程。
本次新增内容以文档、环境模板和契约测试为主,没有新增 MQ 运行时代码或执行流。
@@ -0,0 +1,26 @@
# GitNexus 开发前基线
- 功能编号:GOV-008
- 记录时间:2026年8月13日 GMT+8 13:59:16
- 分支:main
- 基线提交:3cc00de8f65e
## 工作区
```text
(干净)
```
## GitNexus 状态
```text
Repository: /Users/citrons/Documents/Codex/2026-08-12/wo-x/a-share-analysis
Indexed: 8/13/2026, 1:59:15 PM
Indexed commit: 3cc00de
Current commit: 3cc00de
Status: ✅ up-to-date
```
## 后续人工分析
在修改代码前,将 GitNexus query、context、impact 的结论写入 `gitnexus/impact-plan.md`。高风险结果必须先获得用户确认。
@@ -0,0 +1,14 @@
# GitNexus 前后对比
## 开发前
- 基线提交:`3cc00de`
- RabbitMQ 仅存在于文档、模板和契约测试,没有运行时代码或执行流。
## 开发后
- 当前活动架构统一为 RocketMQ 5.x。
- 新增 MessageBusAdapter、Topic/Tag/Group、重试、死信、幂等和顺序键约束。
- GitNexus 变更检测为 LOW,0 条执行流受影响。
结论:调整发生在实现客户端之前,避免了双栈迁移;后续真实风险集中在 Node 客户端与 RocketMQ 5.3.2 的契约验证。
@@ -0,0 +1,10 @@
# GitNexus 变更检测
- 检测日期:2026-08-13
- 范围:unstaged
- 变更文件:16 个已跟踪文件,另含 GOV-008 功能档案与 ADR 新文件。
- 变更符号:35
- 受影响流程:0
- 风险:LOW
变更集中在当前架构、环境模板、部署说明、代码规范和环境契约测试。GitNexus 未发现市场业务、数据库、HTTP 或消息运行流程受影响,因为 Server 和 MQ 客户端尚未实现。
@@ -0,0 +1,13 @@
# GitNexus 开发前影响分析
- 分析日期:2026-08-13
- 基线提交:`3cc00de`
- 索引状态:up-to-date
查询 RabbitMQ、消息队列、Scheduler、Worker 和部署配置。当前仓库没有 Server 生产代码或 MQ 执行流,影响集中在系统架构、代码开发规范、环境模板、部署文档和环境契约测试。
- `environment-baseline.test.mjs`:LOW,无上游调用方或业务流程。
- `system-architecture.md`:LOW,直接引用方为 README 与文档索引。
- `code-development-standards.md`:LOW,直接引用方为 README 与文档索引。
结论:不影响市场业务、数据库或 HTTP 运行流程。主要风险是环境键漂移和把 NameServer 可达误判为消息链路可用,分别由契约测试和分层连接证据控制。
@@ -0,0 +1,13 @@
# GOV-008 实施计划
- [x] 封存并推送 `v0.0.6` 开发库 N/A Schema 基线。
- [x] 执行 `governance:start` 与 GitNexus 开发前探索和影响分析。
- [x] 先修改环境契约测试,确认旧 RabbitMQ 配置使测试失败。
- [x] 将 Scheduler/Worker 开发和生产模板改为 `ROCKETMQ_*`。
- [x] 更新系统架构、代码开发规范、部署文档和未发布市场概览 PRD。
- [x] 补齐 GOV-008 功能档案和本机连接验证证据。
- [x] 运行治理、MySQL、环境、Schema 测试和配置/文档检查。
- [x] 执行 GitNexus 变更检测、代码 Review 与规范 Review。
- [x] 执行 `governance:finish`,中文提交并推送 Gitea。
本版本不实现客户端、不创建 Broker/Proxy、不发送测试消息。后续 Server 脚手架功能负责选型 Node 客户端并完成真实 Topic 收发契约测试。
@@ -0,0 +1,18 @@
id: GOV-008
name: RocketMQ 消息队列架构调整
slug: rocketmq-adoption
status: released
owner: project
created_at: 2026-08-13
prd: prd.md
requirements: requirements.md
design: design.md
implementation_plan: implementation-plan.md
test_plan: test-plan.md
gitnexus_before: gitnexus/before.md
gitnexus_after: gitnexus/after.md
gitnexus_comparison: gitnexus/comparison.md
code_review: review/code-review.md
standards_review: review/standards-review.md
implementation_commit: pending
release: v0.0.7
@@ -0,0 +1,33 @@
# RocketMQ 消息队列架构调整 PRD
- 功能编号:GOV-008
- PRD 版本:0.1
- 状态:approved
- 日期:2026-08-13
## 问题
项目早期约定 RabbitMQ,但尚未实现消息客户端或业务消息链路。现决定统一采用 RocketMQ,并需要在服务脚手架落地前同步架构、环境契约和部署边界,避免后续同时维护两套消息模型。
## 目标
- 项目唯一消息队列调整为 RocketMQ 5.x。
- Scheduler 和 Worker 通过 `MessageBusAdapter` 使用版本化消息契约。
- 配置明确区分 NameServer、Broker/Proxy Endpoint、Topic 和 Group。
- 保留本地默认关闭策略,同时允许连接独立运行的本机 RocketMQ。
- 检查当前本机 Docker RocketMQ 的实际连通状态,不夸大验证范围。
## 非目标
- 本版本不实现 Server、Scheduler、Worker 或 RocketMQ Node 客户端。
- 不替用户创建、删除或重启 Docker 或本机 RocketMQ 进程。
- 不连接生产 RocketMQ,不创建生产 Topic 或 Group。
- NameServer 可达时不宣称消息生产和消费已经可用。
## 验收标准
- 当前架构、开发规范、部署说明和环境模板只采用 RocketMQ。
- Scheduler 与 Worker 开发/生产模板键集合分别一致,且无 RabbitMQ 变量。
- 文档定义重复投递、幂等、重试、死信和顺序消息边界。
- 本机 NameServer 的版本、端口和启动状态有可复核证据。
- 诊断必须区分 Docker 容器与本机原生进程,并报告 NameServer、Broker、Proxy 的真实状态。
@@ -0,0 +1,18 @@
# 测试结果
- 日期:2026-08-13
- 状态:passed
测试先行阶段,环境契约测试按预期因缺少 `ROCKETMQ_*` 和活动文档仍包含 RabbitMQ 而失败。实现后结果:
- 治理测试:5/5 passed。
- MySQL 5.7 基线测试:2/2 passed。
- 环境与部署契约测试:7/7 passed。
- Schema 基线测试:23/23 passed。
- `npm run schema:verify`:`v0.0.6 (not_applicable)` passed。
- `git diff --check` 与 Node 语法检查:passed。
- 活动架构、部署、治理规范与环境模板 RabbitMQ 残留扫描:passed。
- `npm run governance:finish -- GOV-008 rocketmq-adoption`:passed。
- 独立代码 Review 与开发规范 Review:passed。
本机诊断确认:Docker `rmqnamesrv` 容器曾启动成功但现已以 137 退出,且非 OOM;宿主机原生 RocketMQ 5.3.2 正在运行 NameServer、`broker-a` 和 Proxy。`9876`、`10911`、`8081` 可达,`mqadmin` 可读取集群、Topic 和 Broker 状态。项目 Node 客户端尚未实现,因此未创建 Topic 或写入测试消息。
@@ -0,0 +1,5 @@
# 视觉 QA
本功能没有页面、组件、样式或浏览器交互变化,视觉 QA 不适用。
结论:not_applicable
@@ -0,0 +1,6 @@
# 发布说明
- 项目消息队列技术基线由 RabbitMQ 调整为 RocketMQ 5.x。
- Scheduler 和 Worker 配置切换为 `ROCKETMQ_*`,并明确 Topic、Group、重试和接入地址。
- 增加 `MessageBusAdapter` 边界,避免业务绑定具体客户端。
- 本机原生 RocketMQ 5.3.2 NameServer、Broker 和 Proxy 已验证可达,Broker 已注册且状态正常;Docker `rmqnamesrv` 容器已退出,项目消息收发待 Node 客户端落地后验证。
@@ -0,0 +1,29 @@
# 工程需求
## 技术基线
- MQ 固定为 RocketMQ 5.x,当前本机镜像版本为 `apache/rocketmq:5.3.2`。
- 业务模块只依赖 `MessageBusAdapter` 与版本化事件,不直接引用客户端 SDK 类型。
- Scheduler 只发布任务,Worker 只消费执行;重复投递由幂等键和业务唯一约束处理。
## 配置契约
- Scheduler 使用 `ROCKETMQ_ENABLED`、`ROCKETMQ_NAMESRV_ADDR`、`ROCKETMQ_ENDPOINT`、`ROCKETMQ_TOPIC_PREFIX` 和发送超时。RocketMQ 5.x Proxy 官方 Node Producer 不要求 Producer Group,禁止把旧 Remoting 参数混入配置。
- Worker 使用相同的基础连接字段,并增加 `ROCKETMQ_CONSUMER_GROUP` 和客户端消费并发。
- 开发和生产模板的同一进程必须保持相同键集合;生产连接地址留空且不得提交真实值。
- `ROCKETMQ_ENABLED=false` 时不建立连接;启用后 Endpoint、Topic 和对应 Group 必须通过启动配置 Schema 校验。
## 消息语义
- 事件必须包含 `eventId`、`eventType`、`schemaVersion`、`occurredAt`、`tradeDate`、`traceId` 和 `idempotencyKey`。
- Topic 表达稳定业务域,Tag 表达事件类型,Consumer Group 表达独立消费进度。
- 需要顺序处理时必须声明消息组键;普通消息不保证全局顺序。
- 超过最大消费次数后进入死信,必须支持查询、告警和受控重放。
- 最大投递次数属于 RocketMQ 服务端 Consumer Group 重试策略,由运维配置并纳入部署验收,不作为官方 Node PushConsumer 的客户端环境变量。
## 本机验证
- 检查容器、镜像、端口映射和 NameServer 启动日志。
- 使用 TCP 探针验证 `127.0.0.1:9876`。
- 检查 Docker 容器和本机原生进程,避免把相同端口的其他监听者误判为目标容器。
- 通过 `mqadmin clusterList`、`topicList`、`brokerStatus` 确认 Broker 注册、版本和管理链路;不擅自创建 Topic 或写入测试消息。
@@ -0,0 +1,22 @@
# 代码 Review
## 第一轮
结论:changes_required。发现 2 个 Important:配置混入旧 Remoting Producer Group 和无效的客户端最大重试字段;RabbitMQ 残留断言只覆盖生产 Worker。
## 处置
- 删除 `ROCKETMQ_PRODUCER_GROUP` 和 `ROCKETMQ_MAX_RECONSUME_TIMES`。
- 明确最大投递次数属于服务端 Consumer Group 运维策略。
- 扩展测试覆盖 Scheduler/Worker 的开发与生产四份模板,同时核对开发和生产连接地址边界。
## 最终复审
- 四份 Scheduler/Worker 模板均移除旧 RabbitMQ 字段、旧 Remoting Producer Group 和无效的客户端最大重试字段。
- 开发模板统一使用 `127.0.0.1:9876` 和 `127.0.0.1:8081` 且保持关闭;生产连接地址保持空值。
- 最大投递次数归属服务端 Consumer Group 运维策略,文档和配置语义一致。
- 环境 7/7、治理 5/5、MySQL 2/2、Schema 23/23 及 Schema 验证通过。
Critical、Important、Minor 均无未关闭项。
最终结论:passed
@@ -0,0 +1,12 @@
# 开发规范符合性 Review
## 检查结果
- PRD、需求、设计、实施计划、测试计划、回退和 GitNexus 档案齐全。
- 技术基线统一为 RocketMQ 5.x,历史档案保留原版本事实。
- 配置模板无秘密,开发与生产同进程键集合一致,生产连接地址为空。
- Node.js 接入边界明确采用 MessageBusAdapter,未提前绑定未经验证的 SDK 参数。
- 本机诊断只读执行,未创建 Topic、未发送消息、未修改或重启用户进程。
- 测试先行、GitNexus 影响分析和独立复审证据齐全。
最终结论:passed
@@ -0,0 +1,7 @@
# 回退说明
本功能只修改架构约定、无秘密环境模板、文档和契约测试,没有启动、停止或修改用户的 Docker 容器,也没有连接生产服务。
若后续 RocketMQ 客户端验证失败,使用新的治理功能和中文提交调整 Adapter 实现或接入方式;不得在业务代码中临时绕过 Adapter。回退文档配置时可以恢复上一已发布提交,但必须同步恢复开发/生产模板和契约测试,禁止形成一半 RabbitMQ、一半 RocketMQ 的混合状态。
本机 `rmqnamesrv` 容器及其 Docker 网络属于用户环境,不由仓库回退操作管理。
@@ -0,0 +1,19 @@
# 测试计划
## 自动化
- 开发和生产同进程环境键一致。
- 生产模板禁止模拟数据并保持 MySQL 5.7 与 RocketMQ 开关契约。
- 活动架构和部署文档包含 RocketMQ 且不再包含 RabbitMQ。
- 本地 Compose 仍只启动 Redis,不隐式增加 RocketMQ 生命周期。
- 治理、MySQL 5.7、Schema 基线回归测试全部通过。
## 本机诊断
- `docker ps` 与本机进程检查 NameServer、Broker、Proxy,定位端口真实监听者。
- `docker inspect rmqnamesrv` 核对镜像与端口映射。
- `docker logs rmqnamesrv` 核对启动成功日志。
- `nc` 检查 `9876`、`10911` 和 `8081` TCP 可达。
- `mqadmin clusterList`、`topicList`、`brokerStatus` 检查 Broker 注册、Topic 路由与运行状态。
基础设施管理链路已验证。由于项目 Server 与 Node 客户端尚未实现,本版本不创建测试 Topic,也不执行项目消息生产和消费,并将此限制记录为未执行项。
+14 -9
View File
@@ -2,13 +2,13 @@
## 1. 目标 ## 1. 目标
本规范保证代码对普通开发者可连续阅读、对关键业务可解释、对历史版本可维护。规范适用于 Vue 3 前端、NestJS 后端、调度器、RabbitMQ Worker、数据适配器、规则引擎和 AI 分析模块。 本规范保证代码对普通开发者可连续阅读、对关键业务可解释、对历史版本可维护。规范适用于 Vue 3 前端、NestJS 后端、调度器、RocketMQ Worker、数据适配器、规则引擎和 AI 分析模块。
## 2. 技术基线 ## 2. 技术基线
- 前端:Vue 3、Vite、TypeScript、Dart Sass、Naive UI。 - 前端:Vue 3、Vite、TypeScript、Dart Sass、Naive UI。
- 后端:NestJS、TypeScript。 - 后端:NestJS、TypeScript。
- 数据库:MySQL 5.7.x,字符集 `utf8mb4`,排序规则 `utf8mb4_general_ci`;缓存:Redis;消息队列:RabbitMQ。 - 数据库:MySQL 5.7.x,字符集 `utf8mb4`,排序规则 `utf8mb4_general_ci`;缓存:Redis;消息队列:RocketMQ 5.x。
- 图表:Lightweight Charts负责K线和分时,ECharts负责热力图、分布和资金趋势。 - 图表:Lightweight Charts负责K线和分时,ECharts负责热力图、分布和资金趋势。
- AI:由服务端调用兼容 OpenAI 格式的付费 API,密钥不得下发浏览器。 - AI:由服务端调用兼容 OpenAI 格式的付费 API,密钥不得下发浏览器。
@@ -51,7 +51,7 @@ Vue 页面可以包含与页面强绑定的小型展示逻辑;当子区域拥
- 对外导出的公共接口、类型和关键函数。 - 对外导出的公共接口、类型和关键函数。
- 涨跌停、复权、停牌、午间休市等 A 股特殊规则。 - 涨跌停、复权、停牌、午间休市等 A 股特殊规则。
- 指标公式、阈值来源、单位、数据日期和缺失值处理。 - 指标公式、阈值来源、单位、数据日期和缺失值处理。
- RabbitMQ 的幂等、重试、死信和消息顺序假设。 - RocketMQ 的 Topic、Tag、Group、幂等、重试、死信和消息顺序假设。
- Redis 与 MySQL 一致性边界和缓存失效策略。 - Redis 与 MySQL 一致性边界和缓存失效策略。
- AI 输入裁剪、事实约束、降级和成本控制逻辑。 - AI 输入裁剪、事实约束、降级和成本控制逻辑。
- 看似可以简化但因兼容性或历史原因必须保留的实现。 - 看似可以简化但因兼容性或历史原因必须保留的实现。
@@ -70,7 +70,9 @@ count += 1;
- 禁止无理由使用 `any`;外部输入先以 `unknown` 接收并校验。 - 禁止无理由使用 `any`;外部输入先以 `unknown` 接收并校验。
- HTTP API 使用版本化 OpenAPI 契约。 - HTTP API 使用版本化 OpenAPI 契约。
- RabbitMQ 事件包含 `eventId`、`eventType`、`schemaVersion`、`occurredAt`、`tradeDate`、`traceId` 和 `idempotencyKey`。 - RocketMQ 事件包含 `eventId`、`eventType`、`schemaVersion`、`occurredAt`、`tradeDate`、`traceId` 和 `idempotencyKey`。
- Topic 使用稳定的业务域命名,环境隔离由部署配置的前缀完成;Tag 表达同一 Topic 内的事件类型,Consumer Group 表达独立消费进度,禁止把临时实例名作为 Group。RocketMQ 5.x Proxy Producer 不配置旧 Remoting Producer Group。
- 需要顺序处理的消息必须明确消息组键,例如证券代码或交易日;未声明顺序键时不得依赖全局到达顺序。
- 金额、成交额和比率必须明确单位,避免同一字段混用元、万元、亿元或百分比小数。 - 金额、成交额和比率必须明确单位,避免同一字段混用元、万元、亿元或百分比小数。
- 数据来源、采集时间、交易日期、完整性和口径版本必须可追溯。 - 数据来源、采集时间、交易日期、完整性和口径版本必须可追溯。
@@ -88,14 +90,14 @@ count += 1;
- API、Scheduler、Worker 必须使用各自的配置 Schema,在进程启动时验证类型、格式、范围和条件必填项;配置错误立即退出。 - API、Scheduler、Worker 必须使用各自的配置 Schema,在进程启动时验证类型、格式、范围和条件必填项;配置错误立即退出。
- 同一进程的开发与生产模板必须保持相同变量名。环境差异通过配置值、依赖注入和 Adapter 收敛,禁止在业务代码中散落 `APP_ENV` 分支。 - 同一进程的开发与生产模板必须保持相同变量名。环境差异通过配置值、依赖注入和 Adapter 收敛,禁止在业务代码中散落 `APP_ENV` 分支。
- 只有 `*.env.example` 可以提交;真实 `.env`、`*.env.local`、密码、Token 和 API Key 必须被 Git 忽略并由运行环境注入。 - 只有 `*.env.example` 可以提交;真实 `.env`、`*.env.local`、密码、Token 和 API Key 必须被 Git 忽略并由运行环境注入。
- Web 只能使用公开的 `VITE_*` 变量。数据库、Redis、RabbitMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。 - Web 只能使用公开的 `VITE_*` 变量。数据库、Redis、RocketMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。
- MySQL、RabbitMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。 - MySQL、RocketMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。
- Docker Compose 与源码部署必须复用同一变量契约,禁止为某种部署方式建立语义不同的别名。 - Docker Compose 与源码部署必须复用同一变量契约,禁止为某种部署方式建立语义不同的别名。
- 生产配置必须位于发布目录之外,切换应用版本不得覆盖配置;日志和诊断输出必须对配置值脱敏。 - 生产配置必须位于发布目录之外,切换应用版本不得覆盖配置;日志和诊断输出必须对配置值脱敏。
## 9. 前端规范 ## 9. 前端规范
- 页面不得直接访问第三方行情源、Redis、RabbitMQ 或 AI 服务。 - 页面不得直接访问第三方行情源、Redis、RocketMQ 或 AI 服务。
- 全局服务器状态使用 TanStack Vue Query,本地界面偏好使用 Pinia或轻量组合式函数。 - 全局服务器状态使用 TanStack Vue Query,本地界面偏好使用 Pinia或轻量组合式函数。
- Sass token集中管理颜色、间距、圆角、字号和断点。 - Sass token集中管理颜色、间距、圆角、字号和断点。
- A 股默认红涨绿跌,但状态必须同时提供文字或形状,不能只依赖颜色。 - A 股默认红涨绿跌,但状态必须同时提供文字或形状,不能只依赖颜色。
@@ -105,8 +107,11 @@ count += 1;
## 10. 后端与任务规范 ## 10. 后端与任务规范
- API、Scheduler和Worker作为独立进程维护,不在请求期间临时抓取全市场数据。 - API、Scheduler和Worker作为独立进程维护,不在请求期间临时抓取全市场数据。
- Scheduler只决定何时投递;RabbitMQ负责任务传输;Worker负责执行;MySQL负责事实记录;Redis负责当前快照和短期状态。 - Scheduler 只决定何时投递;MessageBusAdapter 负责隔离客户端;RocketMQ 负责任务传输;Worker 负责执行;MySQL 负责事实记录;Redis 负责当前快照和短期状态。
- 消费者必须幂等,消息确认只能发生在持久化或可验证处理完成后。 - 消费者必须幂等,消息确认只能发生在持久化或可验证处理完成后。
- RocketMQ 可能重复投递;消费者不得把“收到一次”当作“只执行一次”,必须以 `idempotencyKey` 和业务唯一约束防重。
- 普通重试使用 RocketMQ 消费重试;延迟任务使用受版本支持的定时/延迟消息;超过服务端 Consumer Group 最大投递次数后进入死信,必须可查询、告警和人工重放。
- 业务代码只依赖项目定义的 `MessageBusAdapter` 和版本化事件契约,禁止直接依赖 RocketMQ SDK 类型;客户端、NameServer 或 Proxy 接入方式由基础设施适配层决定。
- 免费数据源通过 Adapter 统一为内部模型,上层不得依赖供应商原始字段。 - 免费数据源通过 Adapter 统一为内部模型,上层不得依赖供应商原始字段。
## 11. MySQL 5.7 兼容规范 ## 11. MySQL 5.7 兼容规范
@@ -137,7 +142,7 @@ count += 1;
- 功能和缺陷修复遵循测试先行:先看到测试因缺少行为而失败,再实现最小代码。 - 功能和缺陷修复遵循测试先行:先看到测试因缺少行为而失败,再实现最小代码。
- 纯规则和指标计算使用单元测试,期望值必须手工推导。 - 纯规则和指标计算使用单元测试,期望值必须手工推导。
- 数据源 Adapter 使用契约测试和固定样本。 - 数据源 Adapter 使用契约测试和固定样本。
- HTTP 与 RabbitMQ 使用契约测试。 - HTTP 与 RocketMQ 使用契约测试;消息测试必须覆盖 Topic、Tag、Group、序列化、重复投递、重试和死信边界。
- 关键页面使用 Playwright流程测试和视觉截图对比。 - 关键页面使用 Playwright流程测试和视觉截图对比。
- 合并前必须通过类型检查、测试、构建和适用的视觉验证。 - 合并前必须通过类型检查、测试、构建和适用的视觉验证。
+2
View File
@@ -14,6 +14,7 @@
- [系统架构](architecture/system-architecture.md) - [系统架构](architecture/system-architecture.md)
- [ADR-0001:单仓库双项目独立部署](adr/ADR-0001-monorepo-independent-deploy.md) - [ADR-0001:单仓库双项目独立部署](adr/ADR-0001-monorepo-independent-deploy.md)
- [ADR-0002:GitNexus 双门禁](adr/ADR-0002-gitnexus-double-gate.md) - [ADR-0002:GitNexus 双门禁](adr/ADR-0002-gitnexus-double-gate.md)
- [ADR-0003:采用 RocketMQ 5.x 作为唯一消息队列](adr/ADR-0003-rocketmq-message-bus.md)
## 环境与部署 ## 环境与部署
@@ -42,5 +43,6 @@
- [GOV-005:MySQL 5.7 字符集与兼容性基线](features/GOV-005-mysql57-charset-baseline/manifest.yaml) - [GOV-005:MySQL 5.7 字符集与兼容性基线](features/GOV-005-mysql57-charset-baseline/manifest.yaml)
- [GOV-006:开发、生产环境与双部署方式](features/GOV-006-environment-deployment-baseline/manifest.yaml) - [GOV-006:开发、生产环境与双部署方式](features/GOV-006-environment-deployment-baseline/manifest.yaml)
- [GOV-007:开发数据库 Schema 基线与版本门禁](features/GOV-007-development-schema-baseline/manifest.yaml) - [GOV-007:开发数据库 Schema 基线与版本门禁](features/GOV-007-development-schema-baseline/manifest.yaml)
- [GOV-008:RocketMQ 消息队列架构调整](features/GOV-008-rocketmq-adoption/manifest.yaml)
功能档案是需求、设计、代码、测试、版本和回退之间的唯一正式关联入口。 功能档案是需求、设计、代码、测试、版本和回退之间的唯一正式关联入口。
+53 -4
View File
@@ -4,7 +4,7 @@ import test from 'node:test';
const repoRoot = new URL('../../', import.meta.url); const repoRoot = new URL('../../', import.meta.url);
const processNames = ['api', 'scheduler', 'worker', 'web']; const processNames = ['api', 'scheduler', 'worker', 'web'];
const sensitiveKeyPattern = /(PASSWORD|API_KEY|TOKEN|SECRET|RABBITMQ_URL)$/; const sensitiveKeyPattern = /(PASSWORD|API_KEY|TOKEN|SECRET)$/;
async function readRepoFile(relativePath) { async function readRepoFile(relativePath) {
return readFile(new URL(relativePath, repoRoot), 'utf8'); return readFile(new URL(relativePath, repoRoot), 'utf8');
@@ -53,7 +53,21 @@ test('模板不包含敏感值且 Web 只暴露 VITE 变量', async () => {
} }
}); });
test('生产模板禁止模拟数据并保持 MySQL 5.7 配置契约', async () => { test('生产模板禁止模拟数据并保持 MySQL 5.7 与 RocketMQ 配置契约', async () => {
const rocketMqTemplates = new Map();
for (const environment of ['development', 'production']) {
for (const processName of ['scheduler', 'worker']) {
rocketMqTemplates.set(
`${environment}/${processName}`,
parseEnvTemplate(
await readRepoFile(`deploy/env/${environment}/${processName}.env.example`),
),
);
}
}
const developmentScheduler = rocketMqTemplates.get('development/scheduler');
const developmentWorker = rocketMqTemplates.get('development/worker');
const productionScheduler = rocketMqTemplates.get('production/scheduler');
const worker = parseEnvTemplate(await readRepoFile('deploy/env/production/worker.env.example')); const worker = parseEnvTemplate(await readRepoFile('deploy/env/production/worker.env.example'));
assert.equal(worker.get('APP_ENV'), 'production'); assert.equal(worker.get('APP_ENV'), 'production');
@@ -61,7 +75,27 @@ test('生产模板禁止模拟数据并保持 MySQL 5.7 配置契约', async ()
assert.equal(worker.get('MYSQL_ENABLED'), 'false'); assert.equal(worker.get('MYSQL_ENABLED'), 'false');
assert.equal(worker.get('MYSQL_CHARSET'), 'utf8mb4'); assert.equal(worker.get('MYSQL_CHARSET'), 'utf8mb4');
assert.equal(worker.get('MYSQL_COLLATION'), 'utf8mb4_general_ci'); assert.equal(worker.get('MYSQL_COLLATION'), 'utf8mb4_general_ci');
assert.equal(worker.get('RABBITMQ_ENABLED'), 'false'); assert.equal(worker.get('ROCKETMQ_ENABLED'), 'false');
assert.equal(worker.get('ROCKETMQ_ENDPOINT'), '');
assert.equal(worker.get('ROCKETMQ_TOPIC_PREFIX'), 'a-share-analysis');
assert.equal(worker.get('ROCKETMQ_CONSUMER_GROUP'), 'a-share-analysis-worker');
for (const [templateName, entries] of rocketMqTemplates) {
assert.equal(
[...entries.keys()].some((key) => key.startsWith('RABBITMQ_')),
false,
`${templateName} 禁止保留 RabbitMQ 变量`,
);
assert.equal(entries.has('ROCKETMQ_PRODUCER_GROUP'), false);
assert.equal(entries.has('ROCKETMQ_MAX_RECONSUME_TIMES'), false);
}
for (const entries of [developmentScheduler, developmentWorker]) {
assert.equal(entries.get('ROCKETMQ_NAMESRV_ADDR'), '127.0.0.1:9876');
assert.equal(entries.get('ROCKETMQ_ENDPOINT'), '127.0.0.1:8081');
}
for (const entries of [productionScheduler, worker]) {
assert.equal(entries.get('ROCKETMQ_NAMESRV_ADDR'), '');
assert.equal(entries.get('ROCKETMQ_ENDPOINT'), '');
}
}); });
test('本地 Compose 仅启动 Redis 并提供健康检查', async () => { test('本地 Compose 仅启动 Redis 并提供健康检查', async () => {
@@ -70,7 +104,22 @@ test('本地 Compose 仅启动 Redis 并提供健康检查', async () => {
assert.match(compose, /services:\s*\n\s+redis:/); assert.match(compose, /services:\s*\n\s+redis:/);
assert.match(compose, /healthcheck:/); assert.match(compose, /healthcheck:/);
assert.match(compose, /127\.0\.0\.1:/); assert.match(compose, /127\.0\.0\.1:/);
assert.doesNotMatch(compose, /^\s{2}(mysql|rabbitmq|api|worker|scheduler|web):/m); assert.doesNotMatch(compose, /^\s{2}(mysql|rocketmq|api|worker|scheduler|web):/m);
});
test('活动架构与部署文档只采用 RocketMQ 5.x', async () => {
for (const relativePath of [
'README.md',
'docs/architecture/system-architecture.md',
'docs/governance/code-development-standards.md',
'docs/deployment/local-development.md',
'docs/deployment/docker-compose-production.md',
'docs/deployment/ubuntu-source-production.md',
]) {
const content = await readRepoFile(relativePath);
assert.match(content, /RocketMQ 5\.x|RocketMQ/);
assert.doesNotMatch(content, /RabbitMQ/);
}
}); });
test('生产源码部署使用独立 EnvironmentFile 并提供 Nginx 代理', async () => { test('生产源码部署使用独立 EnvironmentFile 并提供 Nginx 代理', async () => {