# 业务应用模块创建规则 > 在 `app/` 目录下创建业务模块,按子模块模式拆分。 > `app/` 目录整体不提交到 git。 ## 模块结构 ``` app/ ├── pom.xml # 聚合所有业务模块 └── rui-{领域}/ ├── pom.xml # 领域聚合 POM ├── rui-{领域}-api/ # [可部署] REST API 服务(有 main Class) ├── rui-{领域}-common/ # [库] DTO、枚举、配置、工具 ├── rui-{领域}-core/ # [库] Mapper、Entity、Service(数据库层) ├── rui-{领域}-provider/ # [库] 第三方集成(可选) └── rui-{领域}-task/ # [库] MQ 监听器 + 定时任务(可选) ``` ### 三模块(最小配置) | 模块 | 类型 | 作用 | |------|------|------| | `-api` | 可部署 | REST 控制器、启动类、application.yml | | `-common` | 库 | DTO、VO、枚举、常量、配置属性 | | `-core` | 库 | Mapper、Entity、Service(MyBatis Plus) | ### 五模块(含第三方集成和异步任务) | 模块 | 类型 | 作用 | |------|------|------| | `-provider` | 库(可选) | 第三方 SDK/API 封装(支付、短信、AI 等) | | `-task` | 库(可选) | MQ 监听器、定时任务、异步处理 | ## 包命名 ``` com.rui.{领域}.api # api 模块 com.rui.{领域} # common 模块 com.rui.{领域}.core # core 模块 com.rui.{领域}.provider # provider 模块 com.rui.{领域}.task # task 模块 ``` ## POM 层级 ``` app/pom.xml → parent: rui-parent (不加 relativePath) └── rui-{领域}/pom.xml → parent: rui-app ├── rui-{领域}-api/ → parent: rui-{领域} ├── rui-{领域}-common/ ├── rui-{领域}-core/ ├── rui-{领域}-provider/ (可选) └── rui-{领域}-task/ (可选) ``` > `` 一律不加,Maven 自动从本地仓库查找父 POM。 ## 依赖关系 ``` common ——→ rui-common-core core ———→ rui-common-mybatis, rui-common-security, common api ———→ core, rui-common-security, rui-common-web provider → common task ———→ core, common ``` - **框架依赖**(`rui-common-*`):版本由 `rui-parent` BOM 管理,不加 `` - **内部模块依赖**(`rui-{领域}-*`):加 `${project.version}` ## app/pom.xml 模板 ```xml 4.0.0 com.rui rui-parent 1.0.0-SNAPSHOT rui-app pom rui-app 睿核科技 — 业务应用模块聚合 rui-payment ``` ## api 模块 POM 模板 ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-webmvc com.rui rui-{领域}-core ${project.version} com.rui rui-common-security com.rui rui-common-web org.springframework.boot spring-boot-maven-plugin repackage repackage true ``` ## 启动类模板 ```java package com.rui.{领域}.api; import com.rui.common.security.annotation.EnableResourceServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableResourceServer public class {Domain}ApiApplication { public static void main(String[] args) { SpringApplication.run({Domain}ApiApplication.class, args); } } ``` ## 日志配置模板 api 模块需复制日志配置文件: ```bash cp docs/logback-spring-template.xml \ app/rui-{领域}/rui-{领域}-api/src/main/resources/logback-spring.xml ``` - 无需修改,自动读取 `spring.application.name` 作为日志目录名 - 按级别分文件(debug / info / warn / error) - 控制台彩色输出,dev 环境显示 DEBUG ## 注意事项 1. **`app/` 目录加入 `.gitignore`**,业务模块不提交到框架仓库 2. api 模块需配置 `@EnableResourceServer`(资源服务器模式) 3. **各模块 Bean 通过 `AutoConfiguration.imports` 自动注入**,不在启动类上使用 `scanBasePackages`(参见下方「模块 Bean 注入规范」) 4. Mapper 扫描由 `rui-common-mybatis` 统一处理(`@MapperScan("com.rui.**.mapper")`),各模块无需重复配置 5. 白名单 URL 通过 `security.oauth2.ignore-urls` 在 Nacos 配置 6. 本地开发配置如 `config/application-dev.yml` 也需 gitignore ## Git 规则 业务应用模块(`app/` 目录下的模块)使用**独立的 Git 仓库**管理,与框架主仓库完全分离。 ### 仓库初始化 ```bash cd app/rui-{领域} git init git add . git commit -m "feat(init): 初始化{领域}模块" git remote add origin git@gitee.com:pigeon/rui-{领域}.git git push -u origin master ``` ### 提交规范 与框架主仓库一致: - **格式**:`type(scope): 中文描述` - **type**:`feat`(新功能)、`fix`(修复)、`docs`(文档)、`refactor`(重构)、`chore`(构建) - **示例**:`feat(payment): 添加支付宝下单接口` ### 推送规则 - **本地开发**:修改后自动 `git commit`,但不自动推送 - **推送时机**:由开发者手动执行 `git push`,或在完成阶段性开发后统一推送 - **自动推送阈值**:当未推送提交数 **超过 10 个** 时,自动推送到远程 - **禁止**:将业务模块代码提交到框架主仓库(`app/` 已加入 `.gitignore`) ### 与框架主仓库的关系 ``` rui-framework/ # 框架主仓库(git) ├── backend/ # 提交到框架仓库 ├── docs/ # 提交到框架仓库 ├── app/ # 不提交到框架仓库(.gitignore) │ └── rui-payment/ # 独立 Git 仓库 │ ├── .git/ # 独立的 git 历史 │ └── ... ``` ## 模块 Bean 注入规范 ### `@SpringBootApplication` 禁止扩大扫描范围 **API 启动类上的 `@SpringBootApplication` 不得使用 `scanBasePackages` 参数扩大包扫描范围。** - 错误示例:`@SpringBootApplication(scanBasePackages = {"com.rui.payment"})` - 正确示例:`@SpringBootApplication` 启动类应仅扫描自身所在包(`com.rui.{领域}.api`)及其子包。 ### 跨模块 Bean 通过 AutoConfiguration 注入 其他模块需要暴露给 Spring 容器的 Bean(`@Service`、`@Component`、`@Mapper` 等),**由各模块自行通过 `AutoConfiguration.imports` 注册**。 **实现方式**: 1. 在模块中创建自动配置类(通常在 `config` 包下): ```java package com.rui.{领域}.core.config; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.ComponentScan; @AutoConfiguration @ComponentScan("com.rui.{领域}.core") public class PaymentCoreAutoConfiguration { } ``` 2. 在模块的 `src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 中注册: ``` com.rui.{领域}.core.config.PaymentCoreAutoConfiguration ``` **原则**: - 每个可复用模块(core、provider、task)必须有自己的 `AutoConfiguration` - 启动类只需引入依赖,无需关心其他模块的包路径 - `rui-common-mybatis` 已通过 `AutoConfiguration.imports` 自动注入并包含 `@MapperScan("com.rui.**.mapper")`,各模块无需重复配置 Mapper 扫描 ## Controller 路径规范 ### URL 格式 ``` /{模块}/{功能}/{方法} ``` 例如:`/payment/trade/create`、`/order/refund/query` ### 控制器分类 | 控制器命名 | 路径 | 认证 | 说明 | |-----------|------|------|------| | `XxxController` | `/{模块}/{功能}/*` | 需认证 | 常规业务接口 | | `XxxEntryController` | `/{模块}/entry/**` | 免认证 | 对外入口(收银台、H5 等) | | `XxxNotifyController` | `/{模块}/notify/**` | 免认证 | 第三方回调(支付、消息等) | | `XxxInnerController` | `/{模块}/inner/**` | 通过 `@Inner` 注解控制 | 内部 Feign 调用 | ### Nacos 白名单配置 ```yaml # Spring Boot 4 使用 PathPattern,不支持 /**/ 中间通配 # 需按模块显式列出白名单路径 security: oauth2: ignore-urls: - /payment/entry/** - /payment/notify/** - /user/entry/** - /actuator/** ``` ### 示例 ```java // 常规接口 — 需认证 @RestController @RequestMapping("/payment/trade") public class PaymentTradeController { @PostMapping("/create") public R create(@RequestBody TradeRequest req) { ... } } // 对外入口 — 免认证 @RestController @RequestMapping("/payment/entry") public class PaymentEntryController { @PostMapping("/alipay") public R alipay(@RequestBody PayRequest req) { ... } } // 第三方回调 — 免认证 @RestController @RequestMapping("/payment/notify") public class PaymentNotifyController { @PostMapping("/alipay") public String alipayNotify(HttpServletRequest request) { ... } } // 内部调用 — @Inner 保护 @RestController @RequestMapping("/payment/inner") public class PaymentInnerController { @Inner @GetMapping("/trade/{id}") public Trade getTrade(@PathVariable Long id) { ... } } ```