From f5994ad77ec980175f671475e82c72d9d7e6f979 Mon Sep 17 00:00:00 2001 From: Citrons <909229731@qq.com> Date: Thu, 13 Aug 2026 14:22:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(GOV-008):=20=E9=87=87=E7=94=A8=20RocketMQ?= =?UTF-8?q?=20=E6=B6=88=E6=81=AF=E9=98=9F=E5=88=97=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- CHANGELOG.md | 6 ++ README.md | 2 +- VERSION | 2 +- deploy/env/development/scheduler.env.example | 11 ++-- deploy/env/development/worker.env.example | 10 ++-- deploy/env/production/scheduler.env.example | 9 +-- deploy/env/production/worker.env.example | 10 ++-- docs/adr/ADR-0003-rocketmq-message-bus.md | 31 ++++++++++ docs/architecture/system-architecture.md | 12 ++-- docs/deployment/docker-compose-production.md | 5 +- docs/deployment/local-development.md | 8 ++- docs/deployment/ubuntu-source-production.md | 4 +- .../FEAT-MO-001-market-overview/prd.md | 2 +- .../GOV-008-rocketmq-adoption/design.md | 45 +++++++++++++++ .../gitnexus/after.md | 20 +++++++ .../gitnexus/before.md | 26 +++++++++ .../gitnexus/comparison.md | 14 +++++ .../gitnexus/detected-changes.md | 10 ++++ .../gitnexus/impact-plan.md | 13 +++++ .../implementation-plan.md | 13 +++++ .../GOV-008-rocketmq-adoption/manifest.yaml | 18 ++++++ .../features/GOV-008-rocketmq-adoption/prd.md | 33 +++++++++++ .../qa/test-results.md | 18 ++++++ .../GOV-008-rocketmq-adoption/qa/visual-qa.md | 5 ++ .../release-notes.md | 6 ++ .../GOV-008-rocketmq-adoption/requirements.md | 29 ++++++++++ .../review/code-review.md | 22 +++++++ .../review/standards-review.md | 12 ++++ .../GOV-008-rocketmq-adoption/rollback.md | 7 +++ .../GOV-008-rocketmq-adoption/test-plan.md | 19 +++++++ docs/governance/code-development-standards.md | 23 +++++--- docs/index.md | 2 + .../governance/environment-baseline.test.mjs | 57 +++++++++++++++++-- 34 files changed, 463 insertions(+), 43 deletions(-) create mode 100644 docs/adr/ADR-0003-rocketmq-message-bus.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/design.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/gitnexus/after.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/gitnexus/before.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/gitnexus/comparison.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/gitnexus/detected-changes.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/gitnexus/impact-plan.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/implementation-plan.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/manifest.yaml create mode 100644 docs/features/GOV-008-rocketmq-adoption/prd.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/qa/test-results.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/qa/visual-qa.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/release-notes.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/requirements.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/review/code-review.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/review/standards-review.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/rollback.md create mode 100644 docs/features/GOV-008-rocketmq-adoption/test-plan.md diff --git a/AGENTS.md b/AGENTS.md index 0966f98..a697ab2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ # 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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 69ea086..b04e000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ 本项目采用语义化版本。功能级变化请从 `docs/features/` 按功能编号追溯。 +## 0.0.7 - 2026-08-13 + +- 将项目唯一消息队列技术基线由 RabbitMQ 调整为 RocketMQ 5.x。 +- 建立 MessageBusAdapter、Topic/Group、重试、死信和顺序消息约束。 +- 更新 Scheduler/Worker 环境模板和双部署文档,并记录本机 NameServer 分层连接验证。 + ## 0.0.6 - 2026-08-13 - 建立上一发布版本开发数据库 Schema 基线生成、规范化和自动校验工具。 diff --git a/README.md b/README.md index 1eb6575..11f2c65 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A read-only A-share market overview and analysis tool. - `web/`: Vue 3 market terminal frontend. - `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. The first implementation slice is the market overview screen in `web/`. diff --git a/VERSION b/VERSION index 1750564..5a5831a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.6 +0.0.7 diff --git a/deploy/env/development/scheduler.env.example b/deploy/env/development/scheduler.env.example index a100a54..03e64bb 100644 --- a/deploy/env/development/scheduler.env.example +++ b/deploy/env/development/scheduler.env.example @@ -11,8 +11,9 @@ REDIS_PASSWORD= REDIS_DATABASE=1 REDIS_TLS=false -# 第一阶段关闭 RabbitMQ,任务通过服务内 Adapter 调用。 -RABBITMQ_ENABLED=false -RABBITMQ_URL= -RABBITMQ_EXCHANGE=a-share-analysis -RABBITMQ_PREFETCH=10 +# 第一阶段关闭 RocketMQ,任务通过服务内 MessageBusAdapter 调用。 +ROCKETMQ_ENABLED=false +ROCKETMQ_NAMESRV_ADDR=127.0.0.1:9876 +ROCKETMQ_ENDPOINT=127.0.0.1:8081 +ROCKETMQ_TOPIC_PREFIX=a-share-analysis +ROCKETMQ_SEND_TIMEOUT_MS=3000 diff --git a/deploy/env/development/worker.env.example b/deploy/env/development/worker.env.example index 7be7477..80a1660 100644 --- a/deploy/env/development/worker.env.example +++ b/deploy/env/development/worker.env.example @@ -22,10 +22,12 @@ MYSQL_PASSWORD= MYSQL_CHARSET=utf8mb4 MYSQL_COLLATION=utf8mb4_general_ci -RABBITMQ_ENABLED=false -RABBITMQ_URL= -RABBITMQ_EXCHANGE=a-share-analysis -RABBITMQ_PREFETCH=10 +ROCKETMQ_ENABLED=false +ROCKETMQ_NAMESRV_ADDR=127.0.0.1:9876 +ROCKETMQ_ENDPOINT=127.0.0.1:8081 +ROCKETMQ_TOPIC_PREFIX=a-share-analysis +ROCKETMQ_CONSUMER_GROUP=a-share-analysis-worker +ROCKETMQ_CONSUMER_CONCURRENCY=4 AI_ENABLED=false AI_BASE_URL= diff --git a/deploy/env/production/scheduler.env.example b/deploy/env/production/scheduler.env.example index 33603f1..f0bfa71 100644 --- a/deploy/env/production/scheduler.env.example +++ b/deploy/env/production/scheduler.env.example @@ -10,7 +10,8 @@ REDIS_PASSWORD= REDIS_DATABASE=1 REDIS_TLS=false -RABBITMQ_ENABLED=false -RABBITMQ_URL= -RABBITMQ_EXCHANGE=a-share-analysis -RABBITMQ_PREFETCH=10 +ROCKETMQ_ENABLED=false +ROCKETMQ_NAMESRV_ADDR= +ROCKETMQ_ENDPOINT= +ROCKETMQ_TOPIC_PREFIX=a-share-analysis +ROCKETMQ_SEND_TIMEOUT_MS=3000 diff --git a/deploy/env/production/worker.env.example b/deploy/env/production/worker.env.example index e1baf09..008102a 100644 --- a/deploy/env/production/worker.env.example +++ b/deploy/env/production/worker.env.example @@ -22,10 +22,12 @@ MYSQL_PASSWORD= MYSQL_CHARSET=utf8mb4 MYSQL_COLLATION=utf8mb4_general_ci -RABBITMQ_ENABLED=false -RABBITMQ_URL= -RABBITMQ_EXCHANGE=a-share-analysis -RABBITMQ_PREFETCH=10 +ROCKETMQ_ENABLED=false +ROCKETMQ_NAMESRV_ADDR= +ROCKETMQ_ENDPOINT= +ROCKETMQ_TOPIC_PREFIX=a-share-analysis +ROCKETMQ_CONSUMER_GROUP=a-share-analysis-worker +ROCKETMQ_CONSUMER_CONCURRENCY=4 AI_ENABLED=false AI_BASE_URL= diff --git a/docs/adr/ADR-0003-rocketmq-message-bus.md b/docs/adr/ADR-0003-rocketmq-message-bus.md new file mode 100644 index 0000000..9f9fb06 --- /dev/null +++ b/docs/adr/ADR-0003-rocketmq-message-bus.md @@ -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 客户端选定后补充契约测试。 diff --git a/docs/architecture/system-architecture.md b/docs/architecture/system-architecture.md index 2445ec5..4116a6f 100644 --- a/docs/architecture/system-architecture.md +++ b/docs/architecture/system-architecture.md @@ -7,8 +7,8 @@ ```text web/ Vue 3 前端 server/ NestJS API、Scheduler 与 Worker -contracts/ HTTP 与 RabbitMQ 版本化契约 -deploy/ MySQL、Redis、RabbitMQ 和运行配置 +contracts/ HTTP 与 RocketMQ 版本化契约 +deploy/ MySQL、Redis、RocketMQ 5.x 和运行配置 docs/ 规范、架构、ADR 与功能档案 tools/ 可重复执行的治理和开发工具 ``` @@ -20,7 +20,8 @@ tools/ 可重复执行的治理和开发工具 ```text 免费数据源 → Scheduler 定时投递 - → RabbitMQ + → MessageBusAdapter + → RocketMQ 5.x → 行情采集 Worker → 指标计算 Worker → 规则 / AI / 混合分析 @@ -30,7 +31,8 @@ tools/ 可重复执行的治理和开发工具 ``` - Scheduler只负责触发时间。 -- RabbitMQ只负责任务传递、确认、重试和死信。 +- MessageBusAdapter 隔离业务与 RocketMQ 客户端,禁止业务模块直接依赖 SDK。 +- RocketMQ 只负责任务传递、确认、重试、延迟消息和死信;重复投递由 Worker 幂等处理。 - Worker负责采集、标准化、计算和分析。 - MySQL保存可追溯事实与历史报告。 - Redis保存最新快照、缓存、锁和限流状态。 @@ -52,7 +54,7 @@ tools/ 可重复执行的治理和开发工具 - 应用运行环境只定义 `development` 与 `production`;生产环境禁止模拟数据、热更新、调试端口和开发默认值回退。 - 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 源码部署,两者共用配置契约和进程边界。 - 源码部署由管理员预装 Node.js 22 LTS、pnpm 10、Nginx 和基础设施;项目只验证,不安装或修改系统软件源。 - 源码部署使用不可变发布目录、`a-share` 用户和 systemd 独立进程配置;生产 env 存放在 `/etc/a-share-analysis/`,不随代码版本切换覆盖。 diff --git a/docs/deployment/docker-compose-production.md b/docs/deployment/docker-compose-production.md index 7433d71..ff7a659 100644 --- a/docs/deployment/docker-compose-production.md +++ b/docs/deployment/docker-compose-production.md @@ -7,11 +7,12 @@ - 从 `deploy/env/production/` 准备四份真实配置,存放在服务器 `/etc/a-share-analysis/` 或其他受控目录。 - Compose 通过外部路径加载,不在 YAML、镜像和 Git 中保存秘密。 - 镜像引用必须包含版本或内容摘要,禁止浮动 `latest`。 -- 第一阶段只依赖 Redis;MySQL 和 RabbitMQ 功能开关保持关闭。 +- 第一阶段只依赖 Redis;MySQL 和 RocketMQ 5.x 功能开关保持关闭。 +- 启用 RocketMQ 时优先连接受运维管理的外部集群或独立基础设施 Compose,不把 NameServer、Broker、Proxy 混入应用发布生命周期。 ## 发布与回退 -部署前验证镜像对应已推送提交或标签、环境 Schema、健康检查和数据目录。升级只修改不可变镜像引用;失败时恢复上一完整镜像集合和未改变的外部配置。禁止删除 Redis、MySQL 或 RabbitMQ 数据目录作为应用回退手段。 +部署前验证镜像对应已推送提交或标签、环境 Schema、健康检查和数据目录。升级只修改不可变镜像引用;失败时恢复上一完整镜像集合和未改变的外部配置。禁止删除 Redis、MySQL 或 RocketMQ CommitLog 数据目录作为应用回退手段。 ## 验证 diff --git a/docs/deployment/local-development.md b/docs/deployment/local-development.md index f88b22d..a0e1e64 100644 --- a/docs/deployment/local-development.md +++ b/docs/deployment/local-development.md @@ -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 文件用于生产。 +## 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 和这些模板,不能在本文档中假装应用已经可启动。 diff --git a/docs/deployment/ubuntu-source-production.md b/docs/deployment/ubuntu-source-production.md index a72eab4..57bd016 100644 --- a/docs/deployment/ubuntu-source-production.md +++ b/docs/deployment/ubuntu-source-production.md @@ -1,6 +1,6 @@ # 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 健康端点。失败时切回上一完整目录并重启服务;生产配置不随版本切换覆盖。 项目部署流程禁止连接或读取生产数据库做开发验证。数据库备份属于独立运维职责,应用回退不得删除或覆盖数据。 + +RocketMQ 由运维独立维护 NameServer、Broker 和需要时的 Proxy。应用发布只读取受控的 RocketMQ Endpoint、Topic 前缀与 Group 配置,不负责创建或升级集群,也不得把 Broker 数据目录放进应用发布目录。 diff --git a/docs/features/FEAT-MO-001-market-overview/prd.md b/docs/features/FEAT-MO-001-market-overview/prd.md index 2304d55..f7337b5 100644 --- a/docs/features/FEAT-MO-001-market-overview/prd.md +++ b/docs/features/FEAT-MO-001-market-overview/prd.md @@ -23,7 +23,7 @@ ## 非目标 - 不做自选股、持仓、笔记、交易计划、社区、荐股、跟单和公开组合。 -- 本切片不接正式行情源、RabbitMQ、MySQL、Redis和付费 AI。 +- 本切片不接正式行情源、RocketMQ、MySQL、Redis和付费 AI。 - 本切片不实现用户、邀请、会员、订阅和支付。 - ETF监控、两融数据、大盘云图和实时消息仍是独立一级模块。 diff --git a/docs/features/GOV-008-rocketmq-adoption/design.md b/docs/features/GOV-008-rocketmq-adoption/design.md new file mode 100644 index 0000000..0fb7f33 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/design.md @@ -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 客户端执行消息收发。 diff --git a/docs/features/GOV-008-rocketmq-adoption/gitnexus/after.md b/docs/features/GOV-008-rocketmq-adoption/gitnexus/after.md new file mode 100644 index 0000000..a837c4c --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/gitnexus/after.md @@ -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 运行时代码或执行流。 diff --git a/docs/features/GOV-008-rocketmq-adoption/gitnexus/before.md b/docs/features/GOV-008-rocketmq-adoption/gitnexus/before.md new file mode 100644 index 0000000..c803868 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/gitnexus/before.md @@ -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`。高风险结果必须先获得用户确认。 diff --git a/docs/features/GOV-008-rocketmq-adoption/gitnexus/comparison.md b/docs/features/GOV-008-rocketmq-adoption/gitnexus/comparison.md new file mode 100644 index 0000000..17f366a --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/gitnexus/comparison.md @@ -0,0 +1,14 @@ +# GitNexus 前后对比 + +## 开发前 + +- 基线提交:`3cc00de` +- RabbitMQ 仅存在于文档、模板和契约测试,没有运行时代码或执行流。 + +## 开发后 + +- 当前活动架构统一为 RocketMQ 5.x。 +- 新增 MessageBusAdapter、Topic/Tag/Group、重试、死信、幂等和顺序键约束。 +- GitNexus 变更检测为 LOW,0 条执行流受影响。 + +结论:调整发生在实现客户端之前,避免了双栈迁移;后续真实风险集中在 Node 客户端与 RocketMQ 5.3.2 的契约验证。 diff --git a/docs/features/GOV-008-rocketmq-adoption/gitnexus/detected-changes.md b/docs/features/GOV-008-rocketmq-adoption/gitnexus/detected-changes.md new file mode 100644 index 0000000..7eda7d4 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/gitnexus/detected-changes.md @@ -0,0 +1,10 @@ +# GitNexus 变更检测 + +- 检测日期:2026-08-13 +- 范围:unstaged +- 变更文件:16 个已跟踪文件,另含 GOV-008 功能档案与 ADR 新文件。 +- 变更符号:35 +- 受影响流程:0 +- 风险:LOW + +变更集中在当前架构、环境模板、部署说明、代码规范和环境契约测试。GitNexus 未发现市场业务、数据库、HTTP 或消息运行流程受影响,因为 Server 和 MQ 客户端尚未实现。 diff --git a/docs/features/GOV-008-rocketmq-adoption/gitnexus/impact-plan.md b/docs/features/GOV-008-rocketmq-adoption/gitnexus/impact-plan.md new file mode 100644 index 0000000..c776c71 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/gitnexus/impact-plan.md @@ -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 可达误判为消息链路可用,分别由契约测试和分层连接证据控制。 diff --git a/docs/features/GOV-008-rocketmq-adoption/implementation-plan.md b/docs/features/GOV-008-rocketmq-adoption/implementation-plan.md new file mode 100644 index 0000000..3a1fd88 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/implementation-plan.md @@ -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 收发契约测试。 diff --git a/docs/features/GOV-008-rocketmq-adoption/manifest.yaml b/docs/features/GOV-008-rocketmq-adoption/manifest.yaml new file mode 100644 index 0000000..03d5dd8 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/manifest.yaml @@ -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 diff --git a/docs/features/GOV-008-rocketmq-adoption/prd.md b/docs/features/GOV-008-rocketmq-adoption/prd.md new file mode 100644 index 0000000..c02021d --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/prd.md @@ -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 的真实状态。 diff --git a/docs/features/GOV-008-rocketmq-adoption/qa/test-results.md b/docs/features/GOV-008-rocketmq-adoption/qa/test-results.md new file mode 100644 index 0000000..e3f3db9 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/qa/test-results.md @@ -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 或写入测试消息。 diff --git a/docs/features/GOV-008-rocketmq-adoption/qa/visual-qa.md b/docs/features/GOV-008-rocketmq-adoption/qa/visual-qa.md new file mode 100644 index 0000000..713c324 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/qa/visual-qa.md @@ -0,0 +1,5 @@ +# 视觉 QA + +本功能没有页面、组件、样式或浏览器交互变化,视觉 QA 不适用。 + +结论:not_applicable diff --git a/docs/features/GOV-008-rocketmq-adoption/release-notes.md b/docs/features/GOV-008-rocketmq-adoption/release-notes.md new file mode 100644 index 0000000..82911ad --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/release-notes.md @@ -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 客户端落地后验证。 diff --git a/docs/features/GOV-008-rocketmq-adoption/requirements.md b/docs/features/GOV-008-rocketmq-adoption/requirements.md new file mode 100644 index 0000000..3930024 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/requirements.md @@ -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 或写入测试消息。 diff --git a/docs/features/GOV-008-rocketmq-adoption/review/code-review.md b/docs/features/GOV-008-rocketmq-adoption/review/code-review.md new file mode 100644 index 0000000..fb0a99e --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/review/code-review.md @@ -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 diff --git a/docs/features/GOV-008-rocketmq-adoption/review/standards-review.md b/docs/features/GOV-008-rocketmq-adoption/review/standards-review.md new file mode 100644 index 0000000..362e579 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/review/standards-review.md @@ -0,0 +1,12 @@ +# 开发规范符合性 Review + +## 检查结果 + +- PRD、需求、设计、实施计划、测试计划、回退和 GitNexus 档案齐全。 +- 技术基线统一为 RocketMQ 5.x,历史档案保留原版本事实。 +- 配置模板无秘密,开发与生产同进程键集合一致,生产连接地址为空。 +- Node.js 接入边界明确采用 MessageBusAdapter,未提前绑定未经验证的 SDK 参数。 +- 本机诊断只读执行,未创建 Topic、未发送消息、未修改或重启用户进程。 +- 测试先行、GitNexus 影响分析和独立复审证据齐全。 + +最终结论:passed diff --git a/docs/features/GOV-008-rocketmq-adoption/rollback.md b/docs/features/GOV-008-rocketmq-adoption/rollback.md new file mode 100644 index 0000000..836c875 --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/rollback.md @@ -0,0 +1,7 @@ +# 回退说明 + +本功能只修改架构约定、无秘密环境模板、文档和契约测试,没有启动、停止或修改用户的 Docker 容器,也没有连接生产服务。 + +若后续 RocketMQ 客户端验证失败,使用新的治理功能和中文提交调整 Adapter 实现或接入方式;不得在业务代码中临时绕过 Adapter。回退文档配置时可以恢复上一已发布提交,但必须同步恢复开发/生产模板和契约测试,禁止形成一半 RabbitMQ、一半 RocketMQ 的混合状态。 + +本机 `rmqnamesrv` 容器及其 Docker 网络属于用户环境,不由仓库回退操作管理。 diff --git a/docs/features/GOV-008-rocketmq-adoption/test-plan.md b/docs/features/GOV-008-rocketmq-adoption/test-plan.md new file mode 100644 index 0000000..ce27f1c --- /dev/null +++ b/docs/features/GOV-008-rocketmq-adoption/test-plan.md @@ -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,也不执行项目消息生产和消费,并将此限制记录为未执行项。 diff --git a/docs/governance/code-development-standards.md b/docs/governance/code-development-standards.md index ce7f5ab..2bf75f4 100644 --- a/docs/governance/code-development-standards.md +++ b/docs/governance/code-development-standards.md @@ -2,13 +2,13 @@ ## 1. 目标 -本规范保证代码对普通开发者可连续阅读、对关键业务可解释、对历史版本可维护。规范适用于 Vue 3 前端、NestJS 后端、调度器、RabbitMQ Worker、数据适配器、规则引擎和 AI 分析模块。 +本规范保证代码对普通开发者可连续阅读、对关键业务可解释、对历史版本可维护。规范适用于 Vue 3 前端、NestJS 后端、调度器、RocketMQ Worker、数据适配器、规则引擎和 AI 分析模块。 ## 2. 技术基线 - 前端:Vue 3、Vite、TypeScript、Dart Sass、Naive UI。 - 后端: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负责热力图、分布和资金趋势。 - AI:由服务端调用兼容 OpenAI 格式的付费 API,密钥不得下发浏览器。 @@ -51,7 +51,7 @@ Vue 页面可以包含与页面强绑定的小型展示逻辑;当子区域拥 - 对外导出的公共接口、类型和关键函数。 - 涨跌停、复权、停牌、午间休市等 A 股特殊规则。 - 指标公式、阈值来源、单位、数据日期和缺失值处理。 -- RabbitMQ 的幂等、重试、死信和消息顺序假设。 +- RocketMQ 的 Topic、Tag、Group、幂等、重试、死信和消息顺序假设。 - Redis 与 MySQL 一致性边界和缓存失效策略。 - AI 输入裁剪、事实约束、降级和成本控制逻辑。 - 看似可以简化但因兼容性或历史原因必须保留的实现。 @@ -70,7 +70,9 @@ count += 1; - 禁止无理由使用 `any`;外部输入先以 `unknown` 接收并校验。 - 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,在进程启动时验证类型、格式、范围和条件必填项;配置错误立即退出。 - 同一进程的开发与生产模板必须保持相同变量名。环境差异通过配置值、依赖注入和 Adapter 收敛,禁止在业务代码中散落 `APP_ENV` 分支。 - 只有 `*.env.example` 可以提交;真实 `.env`、`*.env.local`、密码、Token 和 API Key 必须被 Git 忽略并由运行环境注入。 -- Web 只能使用公开的 `VITE_*` 变量。数据库、Redis、RabbitMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。 -- MySQL、RabbitMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。 +- Web 只能使用公开的 `VITE_*` 变量。数据库、Redis、RocketMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。 +- MySQL、RocketMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。 - Docker Compose 与源码部署必须复用同一变量契约,禁止为某种部署方式建立语义不同的别名。 - 生产配置必须位于发布目录之外,切换应用版本不得覆盖配置;日志和诊断输出必须对配置值脱敏。 ## 9. 前端规范 -- 页面不得直接访问第三方行情源、Redis、RabbitMQ 或 AI 服务。 +- 页面不得直接访问第三方行情源、Redis、RocketMQ 或 AI 服务。 - 全局服务器状态使用 TanStack Vue Query,本地界面偏好使用 Pinia或轻量组合式函数。 - Sass token集中管理颜色、间距、圆角、字号和断点。 - A 股默认红涨绿跌,但状态必须同时提供文字或形状,不能只依赖颜色。 @@ -105,8 +107,11 @@ count += 1; ## 10. 后端与任务规范 - 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 统一为内部模型,上层不得依赖供应商原始字段。 ## 11. MySQL 5.7 兼容规范 @@ -137,7 +142,7 @@ count += 1; - 功能和缺陷修复遵循测试先行:先看到测试因缺少行为而失败,再实现最小代码。 - 纯规则和指标计算使用单元测试,期望值必须手工推导。 - 数据源 Adapter 使用契约测试和固定样本。 -- HTTP 与 RabbitMQ 使用契约测试。 +- HTTP 与 RocketMQ 使用契约测试;消息测试必须覆盖 Topic、Tag、Group、序列化、重复投递、重试和死信边界。 - 关键页面使用 Playwright流程测试和视觉截图对比。 - 合并前必须通过类型检查、测试、构建和适用的视觉验证。 diff --git a/docs/index.md b/docs/index.md index a8d976a..0bdbe0e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,6 +14,7 @@ - [系统架构](architecture/system-architecture.md) - [ADR-0001:单仓库双项目独立部署](adr/ADR-0001-monorepo-independent-deploy.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-006:开发、生产环境与双部署方式](features/GOV-006-environment-deployment-baseline/manifest.yaml) - [GOV-007:开发数据库 Schema 基线与版本门禁](features/GOV-007-development-schema-baseline/manifest.yaml) +- [GOV-008:RocketMQ 消息队列架构调整](features/GOV-008-rocketmq-adoption/manifest.yaml) 功能档案是需求、设计、代码、测试、版本和回退之间的唯一正式关联入口。 diff --git a/tools/governance/environment-baseline.test.mjs b/tools/governance/environment-baseline.test.mjs index 70597ac..df45614 100644 --- a/tools/governance/environment-baseline.test.mjs +++ b/tools/governance/environment-baseline.test.mjs @@ -4,7 +4,7 @@ import test from 'node:test'; const repoRoot = new URL('../../', import.meta.url); 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) { 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')); 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_CHARSET'), 'utf8mb4'); 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 () => { @@ -70,7 +104,22 @@ test('本地 Compose 仅启动 Redis 并提供健康检查', async () => { assert.match(compose, /services:\s*\n\s+redis:/); assert.match(compose, /healthcheck:/); 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 () => {