Skip to content

从源码本地开发 ​

这页带你把 IoT DC3 从源码跑起来:先用 Compose 起好 PostgreSQL 与 RabbitMQ,再用一组本地环境变量把 Java 进程指向本机端口,构建、启动、跑测试。读完你能在自己的机器上跑通整套中心服务,并理解为什么启动有先后、为什么本地进程不能直接吃根 .env。只想先扫一眼界面的,demo.dc3.site 有 mock 数据的在线演示,不用本地起栈。

你在这里:想从源码开发或调试。只想点一遍最短闭环,请看 第一个设备:端到端 ;想理解服务怎么拼起来,请看 系统架构总览。

先决条件 ​

本地开发是"半容器"模式:基础设施(数据库、消息中间件)跑在容器里,中心服务以本地 Java 进程运行,方便断点调试与热重启。所以你需要一套 JDK/构建工具,外加一个容器运行时。

  • JDK 21 —— 平台强制 Java 21,低版本编译会直接失败。
  • Maven 3.9+ —— 仓库内置 .mvn/settings.xml 与并行构建配置;多模块打包用它。
  • pnpm —— 前端 dc3-web/ 与同级目录 dc3-cli/ 都用 pnpm(不要用 npm/yarn)。仅做后端开发可跳过。
  • 容器运行时 —— Docker 或 Podman 均可。Makefile 的 COMPOSE 自动探测:docker compose 可用则优先,其次 podman compose;也可以用 COMPOSE='podman compose' 显式指定。

在 JetBrains IDEA 中开发 ​

如果你用 IntelliJ IDEA(Community 或 Ultimate),以下步骤帮你从零配好项目。

1. 打开项目 ​

  1. File → Open(不是 New → Project from Existing Sources)
  2. 选择仓库根目录的 pom.xml
  3. 弹出对话框选 Open as Project
  4. 等待 Maven 索引完成(右下角进度条,首次 2-5 分钟)

2. 安装 EnvFile 插件 ​

  1. Settings → Plugins → Marketplace,搜索 EnvFile,安装后重启
  2. EnvFile 的挂载在下面第 3 步创建运行配置时进行

3. 配置运行入口 ​

打开入口类,点击 main 左侧绿色按钮 → Modify Run Configuration,在 EnvFile 标签页添加 dc3/env/dev.env:

服务入口类模块
GatewayGatewayApplicationdc3-gateway
Auth 中心AuthApplicationdc3-center/dc3-center-auth
Manager 中心ManagerApplicationdc3-center/dc3-center-manager
Data 中心DataApplicationdc3-center/dc3-center-data
Agentic 中心AgenticApplicationdc3-center/dc3-center-agentic

4. 启动顺序 ​

  1. Auth 中心(8300)→ 2. Manager 中心(8400)→ 3. Data 中心(8500)→ 4. Agentic 中心(8600)→ 5. Gateway (8000)

5. 常见问题 ​

  • Lombok 报红:Settings → Annotation Processors → 勾选 Enable annotation processing
  • Maven 索引卡住:File → Invalidate Caches → Invalidate and Restart
  • EnvFile 未生效:检查 Run Configuration 的 EnvFile 标签页是否已勾选 dev.env
  • 端口占用:lsof -i :8000 查看(Windows 用 netstat -ano | findstr :8000),在 Run Configuration 的 Environment variables 中覆盖 SERVER_PORT

命令行五步为什么是这个顺序 ​

本地起栈的最短闭环是五步:起基础设施(容器)→ 加载指向它们的环境变量 → 构建 → 起开发栈 → 跑测试。注意两条路线的分工:make up-dev 起的是全容器 dev 栈,镜像在容器内自行构建(builder 阶段内跑 Maven,不消费宿主机的 jar),首次起栈会完整构建一遍、耗时较长; 第三步的宿主机 make package 服务于 IDEA 本地进程路线(半容器模式)与快速编译验证,与容器路线并列,不是 up-dev 的前置。

先决条件 · 半容器模式(依赖在容器 · Java 在宿主机)起容器编译构建容器端口变量就绪jar 就绪栈已起localhost 指向DB/MQ 须就绪Podman容器运行时JDK 21强制 · 低版本失败Maven 3.9+内置 settings.xmlpnpm前端/CLI · 可跳过① make up-db起 db 栈容器PostgreSQL + RabbitMQ国内源 up-db-cn② source dev.env.sh导出本地变量POSTGRES_HOST=localhost仅当前 shell 生效③ make package多模块构建产物 dc3-*.jar不依赖容器④ make up-dev起开发栈网关 :8000 可登录Auth→…→Gateway 顺序⑤ make test单元测试it 需容器 · e2e 后端改完先跑兜底产出物 · 下一步依赖上一步的产物PostgreSQL :35432RabbitMQ :35672shell 变量仅当前 shelldc3-*.jar各服务可执行 jar网关 :8000登录 token 12hunit 绿it 另需容器常见坑:忘 source dev.env.sh · podman 未启 · 端口被占 · 首启 initdb 慢上线前必换 DC3_SECURITY_KEY / AUTH_HMAC_SECRET(Gateway fail-fast 拒绝启动)先决条件容器 / 构建步骤环境 / 验证起栈产出物

下面逐步展开,每步都给出"做什么"和"怎么验证"。

第一步:起基础设施 ​

make up-db 用 Compose 拉起 db 栈——PostgreSQL 与 RabbitMQ。PostgreSQL 首次启动会按文件名顺序执行 initdb 脚本(扩展、common、auth、data、manager、history、agentic),把租户、用户、菜单、元数据表全部建好,所以第一次起会比后续慢。

bash
make up-db
bash
make up-db-cn

容器对宿主发布的端口是固定的:PostgreSQL localhost:35432、RabbitMQ AMQP localhost:35672(容器内部仍是 5432 / 5672 )。验证:

bash
make ps STACK=db                   # 应看到 dc3-postgres、dc3-rabbitmq 在 running
docker exec dc3-postgres psql -U dc3 -d dc3 -c '\dt dc3_auth.*'   # 能列出 auth 表即就绪(Podman 用户把 docker 换成 podman)

可选可观测栈

需要 EMQX、ELK、Prometheus、Grafana 时,再执行 make up-optional。本地核心开发用不到,建议等中心服务稳定后再起,避免一次性拉起过多容器。

第二步:加载本地环境变量 ​

bash
source dc3/env/dev.env.sh

这一步把数据库、RabbitMQ、MQTT、gRPC 目标主机等开发默认值导出到当前 shell。其中关键的是把 POSTGRES_HOST=localhost、 POSTGRES_PORT=35432、RABBITMQ_HOST=localhost、RABBITMQ_PORT=35672、以及 CENTER_AUTH_HOST/MANAGER_HOST/DATA_HOST/AGENTIC_HOST=localhost 指向本机,让本地 Java 进程能连上第一步发布出来的容器端口。

.env 只给 Docker Compose 插值,不会注入本地 Java 进程

根目录 .env 是 Compose 专用的——它只在 docker compose 解析时做变量插值(镜像仓库、镜像 tag、发布端口),不会自动注入到你本地起的 Java 进程。本地从源码跑,必须 source dc3/env/dev.env.sh;否则服务会沿用容器内 DNS 名(如 dc3-postgres:5432 ),在本机根本解析不到,连接直接失败。

在 JetBrains IDEA 里运行时,用 EnvFile 插件加载不含 export 的 dc3/env/dev.env,或把其键值粘进运行配置的环境变量。

验证:echo $POSTGRES_PORT 应回显 35432。

第三步:构建 ​

bash
make package                       # 等价于 mvn -s .mvn/settings.xml clean package

仓库已配好并行构建、强制 JDK 21/Maven 3.9+、Spotless + Checkstyle 格式与风格校验(本地可用 make format-java 一键格式化)。构建产物是各服务模块的可执行 jar(如 dc3-gateway/target/dc3-gateway.jar)。这一步只验证编译与打包,不依赖第一步的容器。

快速编译检查

只想确认改动能编译、不想整包,用 mvn -s .mvn/settings.xml -q -DskipTests compile,比全量 package 快很多。

第四步:起开发栈 ​

用 dev 栈把中心服务跑起来。make up STACK=dev(或简写 make up-dev)按依赖顺序启动 Gateway、Auth、Manager、Data、Agentic 与驱动。dev 栈的镜像在容器内构建(不依赖第三步在宿主机构建出的 jar),首次起栈较慢。

容器栈要"干净"的 shell:传 RUNTIME_ENV_FILE= 抑制本地进程默认值

dc3/env/dev.env 里的 localhost:35432 等值是给本地 Java 进程用的(第二步),但 Makefile 会自动 include 并 export 这个文件,再经 docker compose 的 ${VAR:-default} 插值灌进容器——中心服务就会去连宿主机端口, 报 Connection refused: localhost/127.0.0.1:35432 并崩溃重启,make up-dev 以 dependency failed to start 收场。 起容器栈时显式抑制:

bash
make up-dev RUNTIME_ENV_FILE=        # 空值即不 include dev.env,容器按 dc3-postgres:5432 等网内地址互连

反过来:第二步的 source dc3/env/dev.env.sh 只在"中心服务跑本地进程"(IDEA / 半容器模式)时才需要;纯容器路线可跳过第二步。 两件事的原理与更多症状见排障。

bash
make up-dev RUNTIME_ENV_FILE=
bash
make up-dev-cn RUNTIME_ENV_FILE=

为什么有先后顺序?因为服务之间有依赖:

  • Auth 中心要先就绪——它持有租户、用户、RBAC 与令牌签发逻辑,其它服务和网关的鉴权都依赖它。
  • Gateway 是唯一对外 HTTP 入口(8000),它聚合 Auth/Manager/Data/Agentic 的路由、抽取鉴权头、注入 principal 上下文。它要在后端中心服务可达之后才能正确转发,所以排在依赖项之后。
完整启动顺序与端口

分布式默认走 gRPC(facade 模式缺省即 grpc,见 Facade 模式)。各服务端口:

服务HTTPgRPC
Gateway / dc3-gateway(唯一对外入口)8000—
鉴权中心 / dc3-center-auth83009300
管理中心 / dc3-center-manager84009400
数据中心 / dc3-center-data85009500
智能中心 / dc3-center-agentic8600—

只有 Gateway(用户入口)与 listening-virtual 的 TCP 6270 / 6271(设备入口;6271 意图是 UDP 通道,但当前发布点未声明 /udp,实际按 TCP 发布,见部署模式与镜像源)对宿主映射,其余后端端口都是内部端口。

验证:栈起来后,对网关跑一次登录黄金路径。登录分两步——先取盐,再连同盐提交明文密码换 12 小时有效的 access token(盐不参与密码哈希,校验在服务端完成):

bash
# 1) 取盐(公开端点,5 分钟内有效)
curl -s -X POST http://localhost:8000/api/v3/auth/token/salt \
  -H 'Content-Type: application/json' \
  -d '{"tenant":"default","name":"dc3"}'      # 2026.9 线返回裸盐;2026.10+ 线返回 {"salt":"a1b2c3d4e5"}

# 2) 连同盐提交明文密码换 token(公开端点,access token 12 小时有效)
curl -s -X POST http://localhost:8000/api/v3/auth/token/generate \
  -H 'Content-Type: application/json' \
  -d '{"tenant":"default","name":"dc3","salt":"<上一步的 salt>","password":"<明文密码>"}'
# 2026.10 镜像线返回 {"token":"eyJhbGciOiJIUzI1NiJ9..."};默认 2026.9 镜像线返回 text/plain 裸 token(同时写入 httpOnly Cookie)

拿到 token 后,受保护端点都通过网关访问,并带上三个鉴权头:X-Auth-Tenant(租户名)、X-Auth-Login(用户名)、X-Auth-Token (JSON 信封 {"salt":"...","token":"..."},携带取盐那一步的 salt;裸 token 会被网关拒绝)。完整的"建驱动 → 建模板 → 建设备 → 读写位号"闭环见 第一个设备:端到端。

第五步:跑测试 ​

bash
make test                          # 单元测试套件

需要更高层验证时:make test-it 跑集成测试(需容器运行时供 Testcontainers),make test-e2e 跑后端 E2E。日常开发改完一处,先 make test 兜住单元回归即可。

常见坑 ​

  • 没 source dev.env.sh 就起服务——最常见。本地 Java 进程拿不到 localhost:35432 / 35672,会去连容器内 DNS 名而连不上。每开一个新 shell 都要重新 source(变量只在当前 shell 生效)。
  • 容器运行时没起——make up-db 报连不上容器运行时,先确认 Docker daemon 或 Podman machine 已启动(macOS 上 Podman 需 podman machine start),再用 make ps STACK=db 确认。
  • 端口被占——35432 / 35672 / 8000 等被其它进程占用时启动失败。释放占用进程,或在根 .env 中覆盖 Compose 发布端口(仅影响容器侧),本地进程端口用服务级环境变量覆盖。
  • 首次起库很慢或表不全——PostgreSQL 只在空卷首启时跑 initdb。若中途中断导致表不全,需重置卷重来:make reset STACK=db(需 CONFIRM_RESET_VOLUMES=true,会删数据,谨慎)。

生产密钥必须替换

dev.env.sh 里的 DC3_SECURITY_KEY 与 AUTH_HMAC_SECRET 是开发默认值。在 pre/pro 环境,若 AUTH_HMAC_SECRET 为空或仍等于默认值 io.github.pnoker.dc3,Gateway 会 fail-fast 拒绝启动。本地开发无妨,上线前务必换成环境专属随机值。

延伸阅读 ​

基于 AGPL-3.0 协议发布