开发概览与规范
这页写给准备给 IoT DC3 写后端代码的开发者:读完你会知道工程的权威规范在哪、命名与分层必须遵守哪些硬约定,以及提交一条改动该走的"第一条路径"。
权威规范在 AGENTS.md
本页是入口与速览,真正的工程规范以仓库根的 iot-dc3/AGENTS.md 为准。它是一份跨 AI 工具共享的单一事实源,覆盖模块分层、Maven 命令、验证流程、提交与变更日志规则、以及下文要点的完整版本。iot-dc3/.claude/CLAUDE.md 只是把规范委托给它,不重复内容。动手前先通读 AGENTS.md;本页与它冲突时以 AGENTS.md 为准。
平台是 Java 21 / Spring Boot 4 / Spring Cloud 2025 的分布式服务,跨服务协调走 gRPC,元数据落 PostgreSQL,异步消息走 RabbitMQ。这套技术栈决定了下面三条不可绕过的约定:CRUD 动词随结果基数走、跨服务调用必须经 facade、领域对象按 DO/BO/VO 分层。
CRUD 动词随"结果基数"走
平台没有自由命名空间:每个 CRUD 形态的方法、HTTP 路径、gRPC RPC、以及前端 API 函数,动词必须反映返回结果的基数——查单条用 get,查集合用 list。这条约定横跨 Service 接口、ServiceImpl、Controller、Local/gRPC Facade、gRPC server 与 .proto 里的 RPC 名,前后端两个仓库一致执行。它的价值是:看到一个方法名或一条路径,不用读实现就知道它返回一条还是一批。
| 动作 | Java 方法 | HTTP 路径 | gRPC RPC | 前端函数 |
|---|---|---|---|---|
| 查单条 | getXxx(...) | /get_xxx | GetXxx | getXxx(...) |
| 查集合 | listXxx(...) | /list_xxx | ListXxx | listXxx(...) |
| 新增 | add(BO) | /add | n/a | addXxx(...) |
| 更新 | update(BO) | /update | n/a | updateXxx(...) |
| 删除 | delete(Long) | /delete | n/a | deleteXxx(...) |
add/delete/update/getById/list(Q) 这五个基础方法由 BaseService<B, Q> 继承而来;子接口只在需要按维度查询时追加 getByXxx/listByXxx,且动词仍要匹配基数。DeviceController 是一个现成范本——它的端点恰好是 /add、/delete、/update、/get_by_id(查单条)、/list_by_ids、/list_by_profile_id、/list(查集合),动词与基数严格对齐。
三个保留动词不要混用
select*只用于*ManagerImpl里对 MyBatis Mapper 的原始调用,不出现在 Service/Controller/Facade 上。remove*只用于 MyBatis-Plus 继承来的 Manager 方法(removeById、remove(wrapper));业务删除一律用delete*。find*、query*、fetch*不作为主 CRUD 动词。
分层调用:Controller(VO) → Service(BO) → Manager(DO) / Facade(跨服务)
请求进来要穿过三层,每层只认一种数据表示。Controller 接收和返回 VO(API 形态);Service 接口继承 BaseService<B, Q>,只在 BO 类型上工作(业务语义,用领域枚举如 EnableFlagEnum);Manager/Mapper 操作 DO(数据库形态,flag 用 Byte)。三种表示之间由 MapStruct 的 *Builder 转换,DO 的 flag 绝不直接泄漏到业务或响应模型。
这里有一条硬边界:当业务代码需要别的服务的数据时,不能直连传输细节,必须经过 facade 接口。Controller 和 Service 类不绑定 gRPC 或 REST 的任何细节——它们只调用 dc3-common-facade-api 里的契约接口,由部署形态决定背后是 gRPC 实现(dc3-common-facade-grpc)还是同进程实现(dc3-common-facade-local-*)。分布式部署默认走 grpc(DC3_FACADE_MODE=grpc)。
图中那条标注"必须经 facade"的虚线就是边界本身:左半边是本服务内的 VO→BO→DO 直落,右半边是任何跨服务读写都要先抽象成 facade 契约、再由配置选择传输实现。这让 grpc(分布式)与 local(单体)成为纯部署拓扑选择,业务代码一行不改。各层对象的字段、枚举转换与 *Builder 的细节见领域模型。
第一条路径:从改一个端点到提交
把上面三条约定串起来,一次典型的后端改动是这样走的。假设你要给设备管理加一个"按 driverId 查设备数量"的接口:
1. VO/BO/DO 在 dc3-common-model 或对应模块补字段(若需要),MapStruct *Builder 同步
2. Manager *ManagerImpl 里用 select* 调 Mapper(仅此处可用 select*)
3. Service 在 Service 接口加 getCountByDriverId(...),ServiceImpl 实现,只碰 BO
4. 跨服务? 若要拿别的中心的数据,走 *Facade 接口,不直连 gRPC
5. Controller GET /get_count_by_driver_id —— 查单值用 get 动词# 快速编译校验(改 Java/共享行为后必跑)
mvn -s .mvn/settings.xml -q -DskipTests compile
# 完整打包(提交前按改动比例选择)
mvn -s .mvn/settings.xml clean package
# 改了 DAL/SQL:需要容器运行时的集成测试
make test-it写完代码、跑过验证,再提交。
提交规范:Conventional Commits
提交信息直接变成发布说明(CHANGE.md 由 git 历史生成),所以 subject 必须具体、可读。格式固定为:
<type>(optional-scope): <english imperative summary>- subject 用英文、小写、祈使句,足够具体以便写进
CHANGE.md。 - 允许的 type:
feat、fix、perf、refactor、docs、build、ci、test、chore、style、security、revert。 - 非根级的微小改动尽量带 scope;破坏性变更用
!并在 body 说明影响。 - 不要用
update、fix bug、change code、misc、wip、.这类弱 subject。
真实示例:
feat(agentic): add session cleanup policy
fix(manager): validate tenant scope for device queries
docs(env): explain JetBrains IDEA environment variables提交前的硬约定
- AI 协作代理未经明确确认不得创建提交;提交前需展示拟用的 commit message 与纳入的文件,等待批准。
- 不要把无关改动塞进一个提交——按意图拆分(功能、修复、重构各自成提交)。
- 发布说明专用提交固定为
docs(release): update generated changelog,且CHANGE.md单独提交。 - 提交前对照上面的格式与真实示例自查 commit message;不合规格式会被 CI 拦在合并前。