2026-01-17 13:05:43 +08:00
|
|
|
|
# RuoYi-Cloud 微服务架构开发规范
|
|
|
|
|
|
|
|
|
|
|
|
> 本文档定义了 RuoYi-Cloud 微服务项目的标准架构、编码规范和最佳实践。
|
|
|
|
|
|
>
|
|
|
|
|
|
> **版本**: v1.0
|
|
|
|
|
|
> **更新日期**: 2026-01-17
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 目录
|
|
|
|
|
|
|
|
|
|
|
|
- [一、项目结构规范](#一项目结构规范)
|
|
|
|
|
|
- [二、Maven配置规范](#二maven配置规范)
|
|
|
|
|
|
- [三、配置文件规范](#三配置文件规范)
|
|
|
|
|
|
- [四、数据库层规范](#四数据库层规范)
|
|
|
|
|
|
- [五、领域层规范](#五领域层domain规范)
|
|
|
|
|
|
- [六、服务层规范](#六服务层service规范)
|
|
|
|
|
|
- [七、控制器层规范](#七控制器层controller规范)
|
|
|
|
|
|
- [八、API层规范](#八api层规范)
|
|
|
|
|
|
- [九、依赖注入规范](#九依赖注入规范)
|
|
|
|
|
|
- [十、命名规范](#十命名规范)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、项目结构规范
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 模块目录结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
ruoyi-modules/{module-name}/
|
|
|
|
|
|
├── src/main/java/com/ruoyi/{module}/
|
|
|
|
|
|
│ ├── controller/ # 控制器层
|
|
|
|
|
|
│ │ ├── {Entity}Controller.java
|
|
|
|
|
|
│ │ └── convert/ # Controller层对象转换
|
|
|
|
|
|
│ ├── service/ # 服务层
|
|
|
|
|
|
│ │ ├── api/ # 服务接口
|
|
|
|
|
|
│ │ │ └── I{Entity}Service.java
|
|
|
|
|
|
│ │ ├── impl/ # 服务实现
|
|
|
|
|
|
│ │ │ └── {Entity}ServiceImpl.java
|
|
|
|
|
|
│ │ ├── dto/ # 数据传输对象
|
|
|
|
|
|
│ │ │ └── {Entity}DTO.java
|
|
|
|
|
|
│ │ └── convert/ # Service层对象转换
|
|
|
|
|
|
│ │ └── {Entity}ServiceConvert.java
|
|
|
|
|
|
│ ├── domain/ # 领域层
|
|
|
|
|
|
│ │ ├── api/ # 领域接口
|
|
|
|
|
|
│ │ │ └── I{Entity}Domain.java
|
|
|
|
|
|
│ │ ├── impl/ # 领域实现
|
|
|
|
|
|
│ │ │ └── {Entity}DomainImpl.java
|
|
|
|
|
|
│ │ ├── model/ # 领域模型
|
|
|
|
|
|
│ │ │ └── {Entity}.java
|
|
|
|
|
|
│ │ └── convert/ # Domain层对象转换
|
|
|
|
|
|
│ │ └── {Entity}DomainConvert.java
|
|
|
|
|
|
│ └── mapper/ # 数据访问层
|
|
|
|
|
|
│ ├── entity/ # 数据库实体
|
|
|
|
|
|
│ │ └── {Entity}Entity.java
|
|
|
|
|
|
│ └── {Entity}Mapper.java # MyBatis Mapper接口
|
|
|
|
|
|
├── src/main/resources/
|
|
|
|
|
|
│ ├── bootstrap.yml # 启动配置
|
|
|
|
|
|
│ └── mapper/{module}/ # MyBatis XML映射文件
|
|
|
|
|
|
│ └── {Entity}Mapper.xml
|
|
|
|
|
|
└── pom.xml # Maven配置
|
|
|
|
|
|
|
|
|
|
|
|
ruoyi-api/{module-name}-api-{submodule}/
|
|
|
|
|
|
├── src/main/java/com/ruoyi/{module}/api/
|
|
|
|
|
|
│ ├── domain/ # VO对象
|
|
|
|
|
|
│ │ └── {Entity}VO.java
|
|
|
|
|
|
│ ├── factory/ # Feign降级工厂
|
|
|
|
|
|
│ │ └── Remote{Module}FallbackFactory.java
|
|
|
|
|
|
│ └── Remote{Module}Service.java # Feign客户端接口
|
|
|
|
|
|
└── pom.xml
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 分层架构说明
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Controller Layer (控制器层)
|
|
|
|
|
|
↓ VO → DTO
|
|
|
|
|
|
Service Layer (服务层)
|
|
|
|
|
|
↓ DTO → Model
|
|
|
|
|
|
Domain Layer (领域层)
|
|
|
|
|
|
↓ Model → Entity
|
|
|
|
|
|
Mapper Layer (数据访问层)
|
|
|
|
|
|
↓
|
|
|
|
|
|
Database (数据库)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**各层职责:**
|
|
|
|
|
|
|
|
|
|
|
|
- **Controller层**: 处理HTTP请求,参数验证,调用Service层,返回响应
|
|
|
|
|
|
- **Service层**: 业务逻辑编排,事务管理,调用Domain层
|
|
|
|
|
|
- **Domain层**: 领域业务逻辑,封装Mapper操作,对象转换
|
|
|
|
|
|
- **Mapper层**: 数据库访问,SQL执行
|
|
|
|
|
|
|
|
|
|
|
|
### 1.3 端口分配规范
|
|
|
|
|
|
|
|
|
|
|
|
| 端口范围 | 用途 | 示例 |
|
|
|
|
|
|
|---------|------|------|
|
|
|
|
|
|
| 9200-9209 | 基础服务 | ruoyi-auth(9200), ruoyi-modules-system(9201), ruoyi-modules-gen(9202), ruoyi-modules-job(9203) |
|
|
|
|
|
|
| 9210-9299 | 业务服务 | tuoheng-device(9210), tuoheng-airline(9211), tuoheng-approval(9212), tuoheng-fms(9213), tuoheng-media(9214), tuoheng-task(9215) |
|
|
|
|
|
|
| 9100 | 监控服务 | ruoyi-visual-monitor(9100) |
|
|
|
|
|
|
| 9300+ | 特殊服务 | ruoyi-modules-file(9300) |
|
|
|
|
|
|
|
|
|
|
|
|
**端口分配原则:**
|
|
|
|
|
|
- 新增业务服务从 9216 开始递增
|
|
|
|
|
|
- 避免端口冲突
|
|
|
|
|
|
- Docker 端口映射必须与 bootstrap.yml 配置一致
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、Maven配置规范
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 根 pom.xml 配置
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.1 添加服务名称常量
|
|
|
|
|
|
|
|
|
|
|
|
在 `ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java` 中添加:
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {模块中文名}服务的serviceid
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static final String {MODULE}_SERVICE = "{module-name}";
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设备服务的serviceid
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static final String DEVICE_SERVICE = "tuoheng-device";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 航线服务的serviceid
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static final String AIRLINE_SERVICE = "tuoheng-airline";
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.2 添加依赖管理
|
|
|
|
|
|
|
|
|
|
|
|
在根 `pom.xml` 的 `<dependencyManagement>` 中添加:
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<!-- {模块中文名}接口 -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>{module-name}-api-{submodule}</artifactId>
|
|
|
|
|
|
<version>${ruoyi.version}</version>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<!-- 设备接口 -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>tuoheng-api-device</artifactId>
|
|
|
|
|
|
<version>${ruoyi.version}</version>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 ruoyi-api/pom.xml 配置
|
|
|
|
|
|
|
|
|
|
|
|
在 `ruoyi-api/pom.xml` 的 `<modules>` 中添加:
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<module>{module-name}-api-{submodule}</module>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<modules>
|
|
|
|
|
|
<module>ruoyi-api-system</module>
|
|
|
|
|
|
<module>tuoheng-api-device</module>
|
|
|
|
|
|
<module>tuoheng-api-airline</module>
|
|
|
|
|
|
</modules>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3 业务模块 pom.xml 标准配置
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
|
|
|
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
|
|
|
|
<parent>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-modules</artifactId>
|
|
|
|
|
|
<version>3.6.7</version>
|
|
|
|
|
|
</parent>
|
|
|
|
|
|
<modelVersion>4.0.0</modelVersion>
|
|
|
|
|
|
|
|
|
|
|
|
<artifactId>{module-name}</artifactId>
|
|
|
|
|
|
|
|
|
|
|
|
<description>
|
|
|
|
|
|
{module-name}系统模块
|
|
|
|
|
|
</description>
|
|
|
|
|
|
|
|
|
|
|
|
<dependencies>
|
|
|
|
|
|
<!-- {Module} API -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>{module-name}-api-{submodule}</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- SpringCloud Alibaba Nacos -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.alibaba.cloud</groupId>
|
|
|
|
|
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- SpringCloud Alibaba Nacos Config -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.alibaba.cloud</groupId>
|
|
|
|
|
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- SpringCloud Alibaba Sentinel -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.alibaba.cloud</groupId>
|
|
|
|
|
|
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- SpringBoot Actuator -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>org.springframework.boot</groupId>
|
|
|
|
|
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Mysql Connector -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.mysql</groupId>
|
|
|
|
|
|
<artifactId>mysql-connector-j</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- RuoYi Common DataSource -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-common-datasource</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- RuoYi Common DataScope -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-common-datascope</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- RuoYi Common Log -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-common-log</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- RuoYi Common Swagger -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-common-swagger</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Flyway Database Migration -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>org.flywaydb</groupId>
|
|
|
|
|
|
<artifactId>flyway-core</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Flyway MySQL Support -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>org.flywaydb</groupId>
|
|
|
|
|
|
<artifactId>flyway-mysql</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
</dependencies>
|
|
|
|
|
|
|
|
|
|
|
|
<build>
|
|
|
|
|
|
<finalName>${project.artifactId}</finalName>
|
|
|
|
|
|
<plugins>
|
|
|
|
|
|
<plugin>
|
|
|
|
|
|
<groupId>org.springframework.boot</groupId>
|
|
|
|
|
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
|
|
|
|
<executions>
|
|
|
|
|
|
<execution>
|
|
|
|
|
|
<goals>
|
|
|
|
|
|
<goal>repackage</goal>
|
|
|
|
|
|
</goals>
|
|
|
|
|
|
</execution>
|
|
|
|
|
|
</executions>
|
|
|
|
|
|
</plugin>
|
|
|
|
|
|
</plugins>
|
|
|
|
|
|
</build>
|
|
|
|
|
|
</project>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2.4 API 模块 pom.xml 标准配置
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
|
|
|
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
|
|
|
|
<parent>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-api</artifactId>
|
|
|
|
|
|
<version>3.6.7</version>
|
|
|
|
|
|
</parent>
|
|
|
|
|
|
<modelVersion>4.0.0</modelVersion>
|
|
|
|
|
|
|
|
|
|
|
|
<artifactId>{module-name}-api-{submodule}</artifactId>
|
|
|
|
|
|
|
|
|
|
|
|
<description>
|
|
|
|
|
|
{module-name} API模块
|
|
|
|
|
|
</description>
|
|
|
|
|
|
|
|
|
|
|
|
<dependencies>
|
|
|
|
|
|
<!-- RuoYi Common Core -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.ruoyi</groupId>
|
|
|
|
|
|
<artifactId>ruoyi-common-core</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Spring Cloud OpenFeign -->
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>org.springframework.cloud</groupId>
|
|
|
|
|
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
</dependencies>
|
|
|
|
|
|
</project>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、配置文件规范
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 bootstrap.yml 标准配置
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
# Tomcat
|
|
|
|
|
|
server:
|
|
|
|
|
|
port: {port} # 按照端口分配规范设置,例如: 9210
|
|
|
|
|
|
|
|
|
|
|
|
# Spring
|
|
|
|
|
|
spring:
|
|
|
|
|
|
application:
|
|
|
|
|
|
# 应用名称
|
|
|
|
|
|
name: {module-name}
|
|
|
|
|
|
profiles:
|
|
|
|
|
|
# 环境配置
|
|
|
|
|
|
active: prod
|
|
|
|
|
|
flyway:
|
|
|
|
|
|
table: flyway_{module}_schema_history # 自定义历史表名
|
|
|
|
|
|
baseline-on-migrate: true # 在nacos中也有配置
|
|
|
|
|
|
baseline-version: 0 # 在nacos中也有配置
|
|
|
|
|
|
cloud:
|
|
|
|
|
|
nacos:
|
|
|
|
|
|
discovery:
|
|
|
|
|
|
# 服务注册地址
|
|
|
|
|
|
server-addr: ruoyi-nacos:8848
|
|
|
|
|
|
config:
|
|
|
|
|
|
# 配置中心地址
|
|
|
|
|
|
server-addr: ruoyi-nacos:8848
|
|
|
|
|
|
# 配置文件格式
|
|
|
|
|
|
file-extension: yml
|
|
|
|
|
|
# 共享配置
|
|
|
|
|
|
shared-configs:
|
|
|
|
|
|
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**配置说明:**
|
|
|
|
|
|
- `server.port`: 必须按照端口分配规范设置
|
|
|
|
|
|
- `spring.application.name`: 服务名称,用于服务注册和发现
|
|
|
|
|
|
- `flyway.table`: Flyway 历史表名,格式为 `flyway_{module}_schema_history`
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 Docker Compose 配置
|
|
|
|
|
|
|
|
|
|
|
|
在 `docker/docker-compose.yml` 中添加服务配置:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
{module-name}-modules-{submodule}:
|
|
|
|
|
|
container_name: {module-name}-modules-{submodule}
|
|
|
|
|
|
image: {module-name}-modules-{submodule}-runtime
|
|
|
|
|
|
build:
|
|
|
|
|
|
context: ./ruoyi/modules/{submodule}
|
|
|
|
|
|
dockerfile: dockerfile
|
|
|
|
|
|
environment:
|
|
|
|
|
|
- TZ=Asia/Shanghai
|
|
|
|
|
|
ports:
|
|
|
|
|
|
- "{port}:{port}" # 宿主机端口:容器端口,必须与bootstrap.yml一致
|
|
|
|
|
|
depends_on:
|
|
|
|
|
|
- ruoyi-redis
|
|
|
|
|
|
- ruoyi-mysql
|
|
|
|
|
|
links:
|
|
|
|
|
|
- ruoyi-redis
|
|
|
|
|
|
- ruoyi-mysql
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**重要提示:**
|
|
|
|
|
|
- Docker 端口映射格式:`宿主机端口:容器端口`
|
|
|
|
|
|
- 容器端口必须与 `bootstrap.yml` 中的 `server.port` 一致
|
|
|
|
|
|
- 宿主机端口不能重复,但容器端口可以相同(因为在不同容器中)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、数据库层规范
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 Flyway 迁移脚本命名规范
|
|
|
|
|
|
|
|
|
|
|
|
**目录结构:**
|
|
|
|
|
|
```
|
|
|
|
|
|
src/main/resources/db/migration/
|
|
|
|
|
|
├── V1__Create_{module}_initial_tables.sql
|
|
|
|
|
|
├── V2__Add_{feature}_tables.sql
|
|
|
|
|
|
├── V3__Modify_{table}_add_{field}.sql
|
|
|
|
|
|
├── V4__Add_{table}_indexes.sql
|
|
|
|
|
|
└── V5__Update_{table}_data.sql
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**命名规则:**
|
|
|
|
|
|
- 格式:`V{版本号}__{描述}.sql`
|
|
|
|
|
|
- 版本号:从1开始递增,不能跳号
|
|
|
|
|
|
- 描述:使用下划线分隔的英文描述,首字母大写
|
|
|
|
|
|
- 操作类型前缀:
|
|
|
|
|
|
- `Create`: 创建表或数据库对象
|
|
|
|
|
|
- `Add`: 添加字段、索引或数据
|
|
|
|
|
|
- `Modify`: 修改表结构或字段
|
|
|
|
|
|
- `Drop`: 删除表或字段
|
|
|
|
|
|
- `Update`: 更新数据
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```
|
|
|
|
|
|
V1__Create_device_tables.sql
|
|
|
|
|
|
V2__Add_device_gateway_field.sql
|
|
|
|
|
|
V3__Add_foreign_key_indexes.sql
|
|
|
|
|
|
V4__Modify_device_add_status_field.sql
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 数据库表设计规范
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS {module}_{table_name} (
|
|
|
|
|
|
{table}_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
|
|
|
|
|
{field_name} VARCHAR(100) COMMENT '字段说明',
|
|
|
|
|
|
-- 继承自BaseEntity的字段
|
|
|
|
|
|
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
|
|
|
|
|
create_time DATETIME COMMENT '创建时间',
|
|
|
|
|
|
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
|
|
|
|
|
update_time DATETIME COMMENT '更新时间',
|
|
|
|
|
|
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
|
|
|
|
|
PRIMARY KEY ({table}_id)
|
|
|
|
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{表中文说明}';
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**设计原则:**
|
|
|
|
|
|
|
|
|
|
|
|
1. **表名格式**: `{module}_{table_name}`,全小写,下划线分隔
|
|
|
|
|
|
- 示例:`device_device`, `device_dock`, `airline_route`
|
|
|
|
|
|
|
|
|
|
|
|
2. **主键命名**: `{table}_id`,类型为 `BIGINT`,`AUTO_INCREMENT`
|
|
|
|
|
|
- 示例:`device_id`, `dock_id`, `route_id`
|
|
|
|
|
|
|
|
|
|
|
|
3. **BaseEntity 字段**: 所有表必须包含以下5个字段
|
|
|
|
|
|
```sql
|
|
|
|
|
|
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
|
|
|
|
|
create_time DATETIME COMMENT '创建时间',
|
|
|
|
|
|
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
|
|
|
|
|
update_time DATETIME COMMENT '更新时间',
|
|
|
|
|
|
remark VARCHAR(500) DEFAULT NULL COMMENT '备注'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
4. **字符集**: 统一使用 `utf8mb4`
|
|
|
|
|
|
|
|
|
|
|
|
5. **存储引擎**: 统一使用 `InnoDB`
|
|
|
|
|
|
|
|
|
|
|
|
6. **注释**: 必须添加表注释和字段注释
|
|
|
|
|
|
|
|
|
|
|
|
7. **外键**: 不使用数据库外键约束,通过应用层维护关联关系
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 索引设计规范
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 外键索引
|
|
|
|
|
|
CREATE INDEX idx_{table}_{foreign_key} ON {module}_{table}({foreign_key});
|
|
|
|
|
|
|
|
|
|
|
|
-- 复合索引
|
|
|
|
|
|
CREATE INDEX idx_{table}_{field1}_{field2} ON {module}_{table}({field1}, {field2});
|
|
|
|
|
|
|
|
|
|
|
|
-- 唯一索引
|
|
|
|
|
|
CREATE UNIQUE INDEX uk_{table}_{field} ON {module}_{table}({field});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**索引命名规则:**
|
|
|
|
|
|
- 普通索引:`idx_{table}_{field}`
|
|
|
|
|
|
- 唯一索引:`uk_{table}_{field}`
|
|
|
|
|
|
- 复合索引:`idx_{table}_{field1}_{field2}`
|
|
|
|
|
|
|
|
|
|
|
|
**索引设计原则:**
|
|
|
|
|
|
- 所有外键字段必须添加索引
|
|
|
|
|
|
- 频繁查询的字段添加索引
|
|
|
|
|
|
- 复合索引遵循最左前缀原则
|
|
|
|
|
|
- 避免过多索引影响写入性能
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 外键索引
|
|
|
|
|
|
CREATE INDEX idx_dock_device_id ON device_dock(device_id);
|
|
|
|
|
|
CREATE INDEX idx_aircraft_device_id ON device_aircraft(device_id);
|
|
|
|
|
|
|
|
|
|
|
|
-- 复合索引
|
|
|
|
|
|
CREATE INDEX idx_dock_device_status ON device_dock(device_id, status);
|
|
|
|
|
|
|
|
|
|
|
|
-- 唯一索引
|
|
|
|
|
|
CREATE UNIQUE INDEX uk_device_sn ON device_device(device_sn);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 Entity 实体类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/mapper/entity/{Entity}Entity.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.mapper.entity;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.web.domain.BaseEntity;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}实体对象 {module}_{table}
|
|
|
|
|
|
* Mapper 层实体,对应数据库表
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}Entity extends BaseEntity
|
|
|
|
|
|
{
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
|
|
|
|
/** 主键ID */
|
|
|
|
|
|
private Long {entity}Id;
|
|
|
|
|
|
|
|
|
|
|
|
/** 字段说明 */
|
|
|
|
|
|
private String fieldName;
|
|
|
|
|
|
|
|
|
|
|
|
public Long get{Entity}Id()
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void set{Entity}Id(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.{entity}Id = {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getFieldName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setFieldName(String fieldName)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.fieldName = fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public String toString()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "{Entity}Entity{" +
|
|
|
|
|
|
"{entity}Id=" + {entity}Id +
|
|
|
|
|
|
", fieldName='" + fieldName + '\'' +
|
|
|
|
|
|
'}';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 必须继承 `BaseEntity`
|
|
|
|
|
|
- 类名格式:`{Entity}Entity`
|
|
|
|
|
|
- 必须实现 `serialVersionUID`
|
|
|
|
|
|
- 使用标准的 Getter/Setter 方法
|
|
|
|
|
|
- 重写 `toString()` 方法
|
|
|
|
|
|
|
|
|
|
|
|
### 4.5 Mapper 接口规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/mapper/{Entity}Mapper.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.mapper;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.mapper.entity.{Entity}Entity;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}Mapper接口
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public interface {Entity}Mapper
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询{表中文说明}列表
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Entity {表中文说明}
|
|
|
|
|
|
* @return {表中文说明}集合
|
|
|
|
|
|
*/
|
|
|
|
|
|
List<{Entity}Entity> select{Entity}List({Entity}Entity {entity}Entity);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID查询{表中文说明}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Id 主键ID
|
|
|
|
|
|
* @return {表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
{Entity}Entity select{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 新增{表中文说明}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Entity {表中文说明}
|
|
|
|
|
|
* @return 结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
int insert{Entity}({Entity}Entity {entity}Entity);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 修改{表中文说明}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Entity {表中文说明}
|
|
|
|
|
|
* @return 结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
int update{Entity}({Entity}Entity {entity}Entity);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除{表中文说明}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Id 主键ID
|
|
|
|
|
|
* @return 结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量删除{表中文说明}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {entity}Ids 主键ID数组
|
|
|
|
|
|
* @return 结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ByIds(Long[] {entity}Ids);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**方法命名规范:**
|
|
|
|
|
|
- 查询列表:`select{Entity}List`
|
|
|
|
|
|
- 查询单个:`select{Entity}ById`
|
|
|
|
|
|
- 新增:`insert{Entity}`
|
|
|
|
|
|
- 修改:`update{Entity}`
|
|
|
|
|
|
- 删除:`delete{Entity}ById`
|
|
|
|
|
|
- 批量删除:`delete{Entity}ByIds`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 4.6 Mapper XML 规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/resources/mapper/{module}/{Entity}Mapper.xml`
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
|
|
|
|
<!DOCTYPE mapper
|
|
|
|
|
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
|
|
|
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
|
|
|
|
<mapper namespace="com.ruoyi.{module}.mapper.{Entity}Mapper">
|
|
|
|
|
|
|
|
|
|
|
|
<resultMap type="com.ruoyi.{module}.mapper.entity.{Entity}Entity" id="{Entity}Result">
|
|
|
|
|
|
<result property="{entity}Id" column="{entity}_id" />
|
|
|
|
|
|
<result property="fieldName" column="field_name" />
|
|
|
|
|
|
<result property="createBy" column="create_by" />
|
|
|
|
|
|
<result property="createTime" column="create_time" />
|
|
|
|
|
|
<result property="updateBy" column="update_by" />
|
|
|
|
|
|
<result property="updateTime" column="update_time" />
|
|
|
|
|
|
<result property="remark" column="remark" />
|
|
|
|
|
|
</resultMap>
|
|
|
|
|
|
|
|
|
|
|
|
<sql id="select{Entity}Vo">
|
|
|
|
|
|
select {entity}_id, field_name, create_by, create_time, update_by, update_time, remark
|
|
|
|
|
|
from {module}_{table}
|
|
|
|
|
|
</sql>
|
|
|
|
|
|
|
|
|
|
|
|
<select id="select{Entity}List" parameterType="com.ruoyi.{module}.mapper.entity.{Entity}Entity" resultMap="{Entity}Result">
|
|
|
|
|
|
<include refid="select{Entity}Vo"/>
|
|
|
|
|
|
<where>
|
|
|
|
|
|
<if test="{entity}Id != null"> and {entity}_id = #{{{entity}Id}}</if>
|
|
|
|
|
|
<if test="fieldName != null and fieldName != ''"> and field_name like concat('%', #{fieldName}, '%')</if>
|
|
|
|
|
|
</where>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<select id="select{Entity}ById" parameterType="Long" resultMap="{Entity}Result">
|
|
|
|
|
|
<include refid="select{Entity}Vo"/>
|
|
|
|
|
|
where {entity}_id = #{{{entity}Id}}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
2026-01-17 17:16:42 +08:00
|
|
|
|
<insert id="insert{Entity}" parameterType="com.ruoyi.{module}.mapper.entity.{Entity}Entity" useGeneratedKeys="true" keyProperty="{entity}Id" keyColumn="{entity}_id">
|
2026-01-17 13:05:43 +08:00
|
|
|
|
insert into {module}_{table}
|
|
|
|
|
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
|
|
|
|
|
<if test="fieldName != null and fieldName != ''">field_name,</if>
|
|
|
|
|
|
<if test="createBy != null and createBy != ''">create_by,</if>
|
|
|
|
|
|
<if test="remark != null">remark,</if>
|
|
|
|
|
|
create_time
|
|
|
|
|
|
</trim>
|
|
|
|
|
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
|
|
|
|
|
<if test="fieldName != null and fieldName != ''">#{fieldName},</if>
|
|
|
|
|
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
|
|
|
|
|
<if test="remark != null">#{remark},</if>
|
|
|
|
|
|
sysdate()
|
|
|
|
|
|
</trim>
|
|
|
|
|
|
</insert>
|
|
|
|
|
|
|
|
|
|
|
|
<update id="update{Entity}" parameterType="com.ruoyi.{module}.mapper.entity.{Entity}Entity">
|
|
|
|
|
|
update {module}_{table}
|
|
|
|
|
|
<trim prefix="SET" suffixOverrides=",">
|
|
|
|
|
|
<if test="fieldName != null and fieldName != ''">field_name = #{fieldName},</if>
|
|
|
|
|
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
|
|
|
|
|
<if test="remark != null">remark = #{remark},</if>
|
|
|
|
|
|
update_time = sysdate()
|
|
|
|
|
|
</trim>
|
|
|
|
|
|
where {entity}_id = #{{{entity}Id}}
|
|
|
|
|
|
</update>
|
|
|
|
|
|
|
|
|
|
|
|
<delete id="delete{Entity}ById" parameterType="Long">
|
|
|
|
|
|
delete from {module}_{table} where {entity}_id = #{{{entity}Id}}
|
|
|
|
|
|
</delete>
|
|
|
|
|
|
|
|
|
|
|
|
<delete id="delete{Entity}ByIds" parameterType="Long">
|
|
|
|
|
|
delete from {module}_{table} where {entity}_id in
|
|
|
|
|
|
<foreach item="{entity}Id" collection="array" open="(" separator="," close=")">
|
|
|
|
|
|
#{{{entity}Id}}
|
|
|
|
|
|
</foreach>
|
|
|
|
|
|
</delete>
|
|
|
|
|
|
|
|
|
|
|
|
</mapper>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 使用 `<resultMap>` 定义结果映射
|
|
|
|
|
|
- 使用 `<sql>` 定义可复用的 SQL 片段
|
|
|
|
|
|
- 使用动态 SQL 标签:`<if>`, `<trim>`, `<foreach>`
|
|
|
|
|
|
- 插入操作使用 `useGeneratedKeys="true"` 返回主键
|
2026-01-17 17:16:42 +08:00
|
|
|
|
- **重要**:插入操作必须同时指定 `keyProperty` 和 `keyColumn` 属性
|
2026-01-17 13:05:43 +08:00
|
|
|
|
- 时间字段使用 `sysdate()` 函数
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、领域层(Domain)规范
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 Model 领域模型规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/domain/model/{Entity}.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.domain.model;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.web.domain.BaseEntity;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}领域模型
|
|
|
|
|
|
* Domain 层模型,用于业务逻辑处理
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity} extends BaseEntity
|
|
|
|
|
|
{
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
|
|
|
|
/** 主键ID */
|
|
|
|
|
|
private Long {entity}Id;
|
|
|
|
|
|
|
|
|
|
|
|
/** 字段说明 */
|
|
|
|
|
|
private String fieldName;
|
|
|
|
|
|
|
|
|
|
|
|
public Long get{Entity}Id()
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void set{Entity}Id(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.{entity}Id = {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getFieldName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setFieldName(String fieldName)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.fieldName = fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public String toString()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "{Entity}{" +
|
|
|
|
|
|
"{entity}Id=" + {entity}Id +
|
|
|
|
|
|
", fieldName='" + fieldName + '\'' +
|
|
|
|
|
|
'}';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 类名不带后缀,直接使用实体名
|
|
|
|
|
|
- 必须继承 `BaseEntity`
|
|
|
|
|
|
- 字段和方法与 Entity 保持一致
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 Domain API 接口规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/domain/api/I{Entity}Domain.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.domain.api;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.domain.model.{Entity};
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}领域接口
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public interface I{Entity}Domain
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询{表中文说明}列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
List<{Entity}> select{Entity}List({Entity} {entity});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID查询{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
{Entity} select{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 新增{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int insert{Entity}({Entity} {entity});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 修改{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int update{Entity}({Entity} {entity});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量删除{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ByIds(Long[] {entity}Ids);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 Domain 实现类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/domain/impl/{Entity}DomainImpl.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.domain.impl;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.domain.api.I{Entity}Domain;
|
|
|
|
|
|
import com.ruoyi.{module}.domain.convert.{Entity}DomainConvert;
|
|
|
|
|
|
import com.ruoyi.{module}.domain.model.{Entity};
|
|
|
|
|
|
import com.ruoyi.{module}.mapper.{Entity}Mapper;
|
|
|
|
|
|
import com.ruoyi.{module}.mapper.entity.{Entity}Entity;
|
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}领域实现
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Component
|
|
|
|
|
|
public class {Entity}DomainImpl implements I{Entity}Domain
|
|
|
|
|
|
{
|
|
|
|
|
|
private final {Entity}Mapper {entity}Mapper;
|
|
|
|
|
|
|
|
|
|
|
|
public {Entity}DomainImpl({Entity}Mapper {entity}Mapper)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.{entity}Mapper = {entity}Mapper;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public List<{Entity}> select{Entity}List({Entity} {entity})
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {Entity}DomainConvert.toEntity({entity});
|
|
|
|
|
|
List<{Entity}Entity> entityList = {entity}Mapper.select{Entity}List(entity);
|
|
|
|
|
|
return {Entity}DomainConvert.toModelList(entityList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public {Entity} select{Entity}ById(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {entity}Mapper.select{Entity}ById({entity}Id);
|
|
|
|
|
|
return {Entity}DomainConvert.toModel(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int insert{Entity}({Entity} {entity})
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {Entity}DomainConvert.toEntity({entity});
|
2026-01-17 17:16:42 +08:00
|
|
|
|
int result = {entity}Mapper.insert{Entity}(entity);
|
|
|
|
|
|
// 【重要】MyBatis 会将自增主键回填到 entity 对象,需要同步回 model 对象
|
|
|
|
|
|
{entity}.set{Entity}Id(entity.get{Entity}Id());
|
|
|
|
|
|
return result;
|
2026-01-17 13:05:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int update{Entity}({Entity} {entity})
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {Entity}DomainConvert.toEntity({entity});
|
|
|
|
|
|
return {entity}Mapper.update{Entity}(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int delete{Entity}ById(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Mapper.delete{Entity}ById({entity}Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int delete{Entity}ByIds(Long[] {entity}Ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Mapper.delete{Entity}ByIds({entity}Ids);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**重要规范:**
|
|
|
|
|
|
- 使用 `@Component` 注解
|
|
|
|
|
|
- 使用**构造器注入**(Constructor Injection)
|
|
|
|
|
|
- 字段声明为 `private final`
|
|
|
|
|
|
- 通过 Convert 类进行对象转换
|
2026-01-17 17:16:42 +08:00
|
|
|
|
- **【关键】insert 方法必须将 Entity 的主键回填到 Model 对象**
|
|
|
|
|
|
|
|
|
|
|
|
**⚠️ MyBatis 主键回填注意事项:**
|
|
|
|
|
|
|
|
|
|
|
|
MyBatis 在执行 INSERT 操作后,会将数据库自动生成的主键值回填到 `Entity` 对象中,但**不会自动同步到传入的 `Model` 对象**。
|
|
|
|
|
|
|
|
|
|
|
|
**错误示例(会导致主键丢失):**
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int insert{Entity}({Entity} {entity})
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {Entity}DomainConvert.toEntity({entity});
|
|
|
|
|
|
return {entity}Mapper.insert{Entity}(entity);
|
|
|
|
|
|
// ❌ 错误:entity 中的主键没有同步回 {entity} 对象
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**正确示例(主键正确回填):**
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int insert{Entity}({Entity} {entity})
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}Entity entity = {Entity}DomainConvert.toEntity({entity});
|
|
|
|
|
|
int result = {entity}Mapper.insert{Entity}(entity);
|
|
|
|
|
|
// ✅ 正确:将 entity 中的主键同步回 {entity} 对象
|
|
|
|
|
|
{entity}.set{Entity}Id(entity.get{Entity}Id());
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**为什么需要手动同步主键?**
|
|
|
|
|
|
|
|
|
|
|
|
1. **对象转换导致的隔离**:`BeanUtils.copyProperties()` 只是属性拷贝,创建了新的 Entity 对象
|
|
|
|
|
|
2. **MyBatis 只回填 Entity**:MyBatis 的 `useGeneratedKeys` 只会将主键设置到 Mapper 方法参数(Entity)中
|
|
|
|
|
|
3. **Model 对象不会自动更新**:原始的 Model 对象与 Entity 对象是两个独立的对象,需要手动同步
|
|
|
|
|
|
|
|
|
|
|
|
**不同步主键的后果:**
|
|
|
|
|
|
|
|
|
|
|
|
如果不将主键同步回 Model 对象,会导致:
|
|
|
|
|
|
- Service 层无法获取新插入记录的主键 ID
|
|
|
|
|
|
- 后续依赖主键的业务逻辑会失败(如关联表插入)
|
|
|
|
|
|
- 可能产生重复数据或数据不一致问题
|
|
|
|
|
|
|
|
|
|
|
|
**示例场景:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
// Service 层调用
|
|
|
|
|
|
Device device = new Device();
|
|
|
|
|
|
device.setDeviceName("测试设备");
|
|
|
|
|
|
deviceDomain.insertDevice(device);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有主键回填,device.getDeviceId() 将返回 null
|
|
|
|
|
|
Long deviceId = device.getDeviceId(); // 期望得到新插入的 ID
|
|
|
|
|
|
|
|
|
|
|
|
// 后续业务逻辑依赖这个 ID
|
|
|
|
|
|
Dock dock = new Dock();
|
|
|
|
|
|
dock.setDeviceId(deviceId); // 如果 deviceId 为 null,会导致关联失败
|
|
|
|
|
|
dockDomain.insertDock(dock);
|
|
|
|
|
|
```
|
2026-01-17 13:05:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.4 Domain Convert 转换类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/domain/convert/{Entity}DomainConvert.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.domain.convert;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.domain.model.{Entity};
|
|
|
|
|
|
import com.ruoyi.{module}.mapper.entity.{Entity}Entity;
|
|
|
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}领域转换类
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}DomainConvert
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Model 转 Entity
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity}Entity toEntity({Entity} model)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (model == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity}Entity entity = new {Entity}Entity();
|
|
|
|
|
|
BeanUtils.copyProperties(model, entity);
|
|
|
|
|
|
return entity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Entity 转 Model
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity} toModel({Entity}Entity entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entity == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity} model = new {Entity}();
|
|
|
|
|
|
BeanUtils.copyProperties(entity, model);
|
|
|
|
|
|
return model;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Entity List 转 Model List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}> toModelList(List<{Entity}Entity> entityList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entityList == null || entityList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}> modelList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity}Entity entity : entityList)
|
|
|
|
|
|
{
|
|
|
|
|
|
modelList.add(toModel(entity));
|
|
|
|
|
|
}
|
|
|
|
|
|
return modelList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Model List 转 Entity List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}Entity> toEntityList(List<{Entity}> modelList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (modelList == null || modelList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}Entity> entityList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity} model : modelList)
|
|
|
|
|
|
{
|
|
|
|
|
|
entityList.add(toEntity(model));
|
|
|
|
|
|
}
|
|
|
|
|
|
return entityList;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 工具类,所有方法为 `public static`
|
|
|
|
|
|
- 使用 `BeanUtils.copyProperties` 进行属性拷贝
|
|
|
|
|
|
- 提供空值检查
|
|
|
|
|
|
- 提供单个对象和列表转换方法
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、服务层(Service)规范
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 DTO 数据传输对象规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/service/dto/{Entity}DTO.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.service.dto;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.web.domain.BaseEntity;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}数据传输对象
|
|
|
|
|
|
* Service 层 DTO,用于业务逻辑编排
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}DTO extends BaseEntity
|
|
|
|
|
|
{
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
|
|
|
|
/** 主键ID */
|
|
|
|
|
|
private Long {entity}Id;
|
|
|
|
|
|
|
|
|
|
|
|
/** 字段说明 */
|
|
|
|
|
|
private String fieldName;
|
|
|
|
|
|
|
|
|
|
|
|
public Long get{Entity}Id()
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void set{Entity}Id(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.{entity}Id = {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getFieldName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setFieldName(String fieldName)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.fieldName = fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public String toString()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "{Entity}DTO{" +
|
|
|
|
|
|
"{entity}Id=" + {entity}Id +
|
|
|
|
|
|
", fieldName='" + fieldName + '\'' +
|
|
|
|
|
|
'}';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 类名格式:`{Entity}DTO`
|
|
|
|
|
|
- 必须继承 `BaseEntity`
|
|
|
|
|
|
- 字段和方法与 Model 保持一致
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 Service API 接口规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/service/api/I{Entity}Service.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.service.api;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.service.dto.{Entity}DTO;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}服务接口
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public interface I{Entity}Service
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询{表中文说明}列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
List<{Entity}DTO> select{Entity}List({Entity}DTO {entity}DTO);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID查询{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
{Entity}DTO select{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 新增{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int insert{Entity}({Entity}DTO {entity}DTO);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 修改{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int update{Entity}({Entity}DTO {entity}DTO);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ById(Long {entity}Id);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量删除{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
int delete{Entity}ByIds(Long[] {entity}Ids);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 Service 实现类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/service/impl/{Entity}ServiceImpl.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.service.impl;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.domain.api.I{Entity}Domain;
|
|
|
|
|
|
import com.ruoyi.{module}.domain.model.{Entity};
|
|
|
|
|
|
import com.ruoyi.{module}.service.api.I{Entity}Service;
|
|
|
|
|
|
import com.ruoyi.{module}.service.convert.{Entity}ServiceConvert;
|
|
|
|
|
|
import com.ruoyi.{module}.service.dto.{Entity}DTO;
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}服务实现
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Service
|
|
|
|
|
|
public class {Entity}ServiceImpl implements I{Entity}Service
|
|
|
|
|
|
{
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private I{Entity}Domain {entity}Domain;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public List<{Entity}DTO> select{Entity}List({Entity}DTO {entity}DTO)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity} model = {Entity}ServiceConvert.toModel({entity}DTO);
|
|
|
|
|
|
List<{Entity}> modelList = {entity}Domain.select{Entity}List(model);
|
|
|
|
|
|
return {Entity}ServiceConvert.toDTOList(modelList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public {Entity}DTO select{Entity}ById(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity} model = {entity}Domain.select{Entity}ById({entity}Id);
|
|
|
|
|
|
return {Entity}ServiceConvert.toDTO(model);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int insert{Entity}({Entity}DTO {entity}DTO)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity} model = {Entity}ServiceConvert.toModel({entity}DTO);
|
|
|
|
|
|
return {entity}Domain.insert{Entity}(model);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int update{Entity}({Entity}DTO {entity}DTO)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity} model = {Entity}ServiceConvert.toModel({entity}DTO);
|
|
|
|
|
|
return {entity}Domain.update{Entity}(model);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int delete{Entity}ById(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Domain.delete{Entity}ById({entity}Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public int delete{Entity}ByIds(Long[] {entity}Ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Domain.delete{Entity}ByIds({entity}Ids);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**重要规范:**
|
|
|
|
|
|
- 使用 `@Service` 注解
|
|
|
|
|
|
- 使用 `@Autowired` 字段注入(Service层使用字段注入)
|
|
|
|
|
|
- 通过 Convert 类进行 DTO ↔ Model 转换
|
|
|
|
|
|
|
|
|
|
|
|
### 6.4 Service Convert 转换类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/service/convert/{Entity}ServiceConvert.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.service.convert;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.domain.model.{Entity};
|
|
|
|
|
|
import com.ruoyi.{module}.service.dto.{Entity}DTO;
|
|
|
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}服务转换类
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}ServiceConvert
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DTO 转 Model
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity} toModel({Entity}DTO dto)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dto == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity} model = new {Entity}();
|
|
|
|
|
|
BeanUtils.copyProperties(dto, model);
|
|
|
|
|
|
return model;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Model 转 DTO
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity}DTO toDTO({Entity} model)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (model == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity}DTO dto = new {Entity}DTO();
|
|
|
|
|
|
BeanUtils.copyProperties(model, dto);
|
|
|
|
|
|
return dto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Model List 转 DTO List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}DTO> toDTOList(List<{Entity}> modelList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (modelList == null || modelList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}DTO> dtoList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity} model : modelList)
|
|
|
|
|
|
{
|
|
|
|
|
|
dtoList.add(toDTO(model));
|
|
|
|
|
|
}
|
|
|
|
|
|
return dtoList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DTO List 转 Model List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}> toModelList(List<{Entity}DTO> dtoList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dtoList == null || dtoList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}> modelList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity}DTO dto : dtoList)
|
|
|
|
|
|
{
|
|
|
|
|
|
modelList.add(toModel(dto));
|
|
|
|
|
|
}
|
|
|
|
|
|
return modelList;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 七、控制器层(Controller)规范
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 Controller 类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/controller/{Entity}Controller.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.controller;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.web.controller.BaseController;
|
|
|
|
|
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
|
|
|
|
|
import com.ruoyi.common.core.web.page.TableDataInfo;
|
|
|
|
|
|
import com.ruoyi.common.log.annotation.Log;
|
|
|
|
|
|
import com.ruoyi.common.log.enums.BusinessType;
|
|
|
|
|
|
import com.ruoyi.common.security.annotation.RequiresPermissions;
|
|
|
|
|
|
import com.ruoyi.{module}.controller.convert.{Entity}ControllerConvert;
|
|
|
|
|
|
import com.ruoyi.{module}.service.api.I{Entity}Service;
|
|
|
|
|
|
import com.ruoyi.{module}.service.dto.{Entity}DTO;
|
|
|
|
|
|
import com.ruoyi.{module}.api.domain.{Entity}VO;
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}控制器
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RestController
|
|
|
|
|
|
@RequestMapping("/{entity}")
|
|
|
|
|
|
public class {Entity}Controller extends BaseController
|
|
|
|
|
|
{
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private I{Entity}Service {entity}Service;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询{表中文说明}列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RequiresPermissions("{module}:{entity}:list")
|
|
|
|
|
|
@GetMapping("/list")
|
|
|
|
|
|
public TableDataInfo list({Entity}VO {entity}VO)
|
|
|
|
|
|
{
|
|
|
|
|
|
startPage();
|
|
|
|
|
|
{Entity}DTO dto = {Entity}ControllerConvert.toDTO({entity}VO);
|
|
|
|
|
|
List<{Entity}DTO> list = {entity}Service.select{Entity}List(dto);
|
|
|
|
|
|
List<{Entity}VO> voList = {Entity}ControllerConvert.toVOList(list);
|
|
|
|
|
|
return getDataTable(voList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取{表中文说明}详细信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RequiresPermissions("{module}:{entity}:query")
|
|
|
|
|
|
@GetMapping(value = "/{id}")
|
|
|
|
|
|
public AjaxResult getInfo(@PathVariable("id") Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}DTO dto = {entity}Service.select{Entity}ById({entity}Id);
|
|
|
|
|
|
{Entity}VO vo = {Entity}ControllerConvert.toVO(dto);
|
|
|
|
|
|
return success(vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 新增{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RequiresPermissions("{module}:{entity}:add")
|
|
|
|
|
|
@Log(title = "{表中文说明}", businessType = BusinessType.INSERT)
|
|
|
|
|
|
@PostMapping
|
|
|
|
|
|
public AjaxResult add(@RequestBody {Entity}VO {entity}VO)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}DTO dto = {Entity}ControllerConvert.toDTO({entity}VO);
|
|
|
|
|
|
return toAjax({entity}Service.insert{Entity}(dto));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 修改{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RequiresPermissions("{module}:{entity}:edit")
|
|
|
|
|
|
@Log(title = "{表中文说明}", businessType = BusinessType.UPDATE)
|
|
|
|
|
|
@PutMapping
|
|
|
|
|
|
public AjaxResult edit(@RequestBody {Entity}VO {entity}VO)
|
|
|
|
|
|
{
|
|
|
|
|
|
{Entity}DTO dto = {Entity}ControllerConvert.toDTO({entity}VO);
|
|
|
|
|
|
return toAjax({entity}Service.update{Entity}(dto));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@RequiresPermissions("{module}:{entity}:remove")
|
|
|
|
|
|
@Log(title = "{表中文说明}", businessType = BusinessType.DELETE)
|
|
|
|
|
|
@DeleteMapping("/{ids}")
|
|
|
|
|
|
public AjaxResult remove(@PathVariable Long[] ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
return toAjax({entity}Service.delete{Entity}ByIds(ids));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 继承 `BaseController`
|
|
|
|
|
|
- 使用 `@RestController` 和 `@RequestMapping`
|
|
|
|
|
|
- 使用 `@Autowired` 字段注入
|
|
|
|
|
|
- 使用 `@RequiresPermissions` 进行权限控制
|
|
|
|
|
|
- 使用 `@Log` 记录操作日志
|
|
|
|
|
|
- 通过 Convert 类进行 VO ↔ DTO 转换
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 Controller Convert 转换类规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `src/main/java/com/ruoyi/{module}/controller/convert/{Entity}ControllerConvert.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.controller.convert;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.{module}.api.domain.{Entity}VO;
|
|
|
|
|
|
import com.ruoyi.{module}.service.dto.{Entity}DTO;
|
|
|
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}控制器转换类
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}ControllerConvert
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* VO 转 DTO
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity}DTO toDTO({Entity}VO vo)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (vo == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity}DTO dto = new {Entity}DTO();
|
|
|
|
|
|
BeanUtils.copyProperties(vo, dto);
|
|
|
|
|
|
return dto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DTO 转 VO
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static {Entity}VO toVO({Entity}DTO dto)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dto == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
{Entity}VO vo = new {Entity}VO();
|
|
|
|
|
|
BeanUtils.copyProperties(dto, vo);
|
|
|
|
|
|
return vo;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DTO List 转 VO List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}VO> toVOList(List<{Entity}DTO> dtoList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dtoList == null || dtoList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}VO> voList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity}DTO dto : dtoList)
|
|
|
|
|
|
{
|
|
|
|
|
|
voList.add(toVO(dto));
|
|
|
|
|
|
}
|
|
|
|
|
|
return voList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* VO List 转 DTO List
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static List<{Entity}DTO> toDTOList(List<{Entity}VO> voList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (voList == null || voList.isEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ArrayList<>();
|
|
|
|
|
|
}
|
|
|
|
|
|
List<{Entity}DTO> dtoList = new ArrayList<>();
|
|
|
|
|
|
for ({Entity}VO vo : voList)
|
|
|
|
|
|
{
|
|
|
|
|
|
dtoList.add(toDTO(vo));
|
|
|
|
|
|
}
|
|
|
|
|
|
return dtoList;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 八、API层规范
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 VO 视图对象规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `ruoyi-api/{module-name}-api-{submodule}/src/main/java/com/ruoyi/{module}/api/domain/{Entity}VO.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.api.domain;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.web.domain.BaseEntity;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {表中文说明}视图对象
|
|
|
|
|
|
* API 层 VO,用于前后端数据交互
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class {Entity}VO extends BaseEntity
|
|
|
|
|
|
{
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
|
|
|
|
/** 主键ID */
|
|
|
|
|
|
private Long {entity}Id;
|
|
|
|
|
|
|
|
|
|
|
|
/** 字段说明 */
|
|
|
|
|
|
private String fieldName;
|
|
|
|
|
|
|
|
|
|
|
|
public Long get{Entity}Id()
|
|
|
|
|
|
{
|
|
|
|
|
|
return {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void set{Entity}Id(Long {entity}Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.{entity}Id = {entity}Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getFieldName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setFieldName(String fieldName)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.fieldName = fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public String toString()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "{Entity}VO{" +
|
|
|
|
|
|
"{entity}Id=" + {entity}Id +
|
|
|
|
|
|
", fieldName='" + fieldName + '\'' +
|
|
|
|
|
|
'}';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 Remote Service 接口规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `ruoyi-api/{module-name}-api-{submodule}/src/main/java/com/ruoyi/{module}/api/Remote{Module}Service.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.api;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
|
|
|
|
|
import com.ruoyi.common.core.constant.ServiceNameConstants;
|
|
|
|
|
|
import com.ruoyi.common.core.domain.R;
|
|
|
|
|
|
import com.ruoyi.{module}.api.domain.{Entity}VO;
|
|
|
|
|
|
import com.ruoyi.{module}.api.factory.Remote{Module}FallbackFactory;
|
|
|
|
|
|
import org.springframework.cloud.openfeign.FeignClient;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestHeader;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {模块中文名}远程服务
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@FeignClient(contextId = "remote{Module}Service", value = ServiceNameConstants.{MODULE}_SERVICE, fallbackFactory = Remote{Module}FallbackFactory.class)
|
|
|
|
|
|
public interface Remote{Module}Service
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID获取{表中文说明}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@GetMapping("/{entity}/{id}")
|
|
|
|
|
|
R<{Entity}VO> get{Entity}ById(@PathVariable("id") Long id, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 使用 `@FeignClient` 注解
|
|
|
|
|
|
- `contextId` 格式:`remote{Module}Service`
|
|
|
|
|
|
- `value` 使用 `ServiceNameConstants` 中定义的常量
|
|
|
|
|
|
- 配置 `fallbackFactory` 降级工厂
|
|
|
|
|
|
- 方法参数必须包含 `@RequestHeader(SecurityConstants.FROM_SOURCE) String source`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 Fallback Factory 降级工厂规范
|
|
|
|
|
|
|
|
|
|
|
|
**位置**: `ruoyi-api/{module-name}-api-{submodule}/src/main/java/com/ruoyi/{module}/api/factory/Remote{Module}FallbackFactory.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package com.ruoyi.{module}.api.factory;
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.core.domain.R;
|
|
|
|
|
|
import com.ruoyi.{module}.api.Remote{Module}Service;
|
|
|
|
|
|
import com.ruoyi.{module}.api.domain.{Entity}VO;
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
import org.springframework.cloud.openfeign.FallbackFactory;
|
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* {模块中文名}服务降级处理
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
|
* @date {date}
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Component
|
|
|
|
|
|
public class Remote{Module}FallbackFactory implements FallbackFactory<Remote{Module}Service>
|
|
|
|
|
|
{
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(Remote{Module}FallbackFactory.class);
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public Remote{Module}Service create(Throwable throwable)
|
|
|
|
|
|
{
|
|
|
|
|
|
log.error("{模块中文名}服务调用失败:{}", throwable.getMessage());
|
|
|
|
|
|
return new Remote{Module}Service()
|
|
|
|
|
|
{
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public R<{Entity}VO> get{Entity}ById(Long id, String source)
|
|
|
|
|
|
{
|
|
|
|
|
|
return R.fail("获取{表中文说明}失败:" + throwable.getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**规范要点:**
|
|
|
|
|
|
- 使用 `@Component` 注解
|
|
|
|
|
|
- 实现 `FallbackFactory<Remote{Module}Service>`
|
|
|
|
|
|
- 记录错误日志
|
|
|
|
|
|
- 返回友好的错误信息
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 九、依赖注入规范
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 依赖注入方式选择
|
|
|
|
|
|
|
|
|
|
|
|
**Domain 层:使用构造器注入(Constructor Injection)**
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Component
|
|
|
|
|
|
public class DeviceDomainImpl implements IDeviceDomain
|
|
|
|
|
|
{
|
|
|
|
|
|
private final DeviceMapper deviceMapper;
|
|
|
|
|
|
|
|
|
|
|
|
public DeviceDomainImpl(DeviceMapper deviceMapper)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.deviceMapper = deviceMapper;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**优点:**
|
|
|
|
|
|
- 依赖不可变(final 字段)
|
|
|
|
|
|
- 更好的可测试性
|
|
|
|
|
|
- 避免循环依赖
|
|
|
|
|
|
- 符合 Spring 官方推荐
|
|
|
|
|
|
|
|
|
|
|
|
**Service 层和 Controller 层:使用字段注入(Field Injection)**
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Service
|
|
|
|
|
|
public class DeviceServiceImpl implements IDeviceService
|
|
|
|
|
|
{
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private IDeviceDomain deviceDomain;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
|
public class DeviceController extends BaseController
|
|
|
|
|
|
{
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private IDeviceService deviceService;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**原因:**
|
|
|
|
|
|
- 代码简洁
|
|
|
|
|
|
- 符合项目现有风格
|
|
|
|
|
|
- 减少样板代码
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 依赖注入最佳实践
|
|
|
|
|
|
|
|
|
|
|
|
1. **Domain 层必须使用构造器注入**
|
|
|
|
|
|
2. **Service 层和 Controller 层使用字段注入**
|
|
|
|
|
|
3. **避免循环依赖**
|
|
|
|
|
|
4. **依赖接口而非实现**
|
|
|
|
|
|
5. **使用 `private` 修饰注入字段**
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十、命名规范
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 包命名规范
|
|
|
|
|
|
|
|
|
|
|
|
| 层级 | 包名 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| 控制器层 | `controller` | HTTP 请求处理 |
|
|
|
|
|
|
| 控制器转换 | `controller.convert` | VO ↔ DTO 转换 |
|
|
|
|
|
|
| 服务接口 | `service.api` | 服务接口定义 |
|
|
|
|
|
|
| 服务实现 | `service.impl` | 服务接口实现 |
|
|
|
|
|
|
| 服务DTO | `service.dto` | 数据传输对象 |
|
|
|
|
|
|
| 服务转换 | `service.convert` | DTO ↔ Model 转换 |
|
|
|
|
|
|
| 领域接口 | `domain.api` | 领域接口定义 |
|
|
|
|
|
|
| 领域实现 | `domain.impl` | 领域接口实现 |
|
|
|
|
|
|
| 领域模型 | `domain.model` | 领域模型对象 |
|
|
|
|
|
|
| 领域转换 | `domain.convert` | Model ↔ Entity 转换 |
|
|
|
|
|
|
| 数据访问 | `mapper` | MyBatis Mapper 接口 |
|
|
|
|
|
|
| 数据实体 | `mapper.entity` | 数据库实体对象 |
|
|
|
|
|
|
| API视图 | `api.domain` | VO 视图对象 |
|
|
|
|
|
|
| API服务 | `api` | Feign 远程服务 |
|
|
|
|
|
|
| API工厂 | `api.factory` | Feign 降级工厂 |
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 类命名规范
|
|
|
|
|
|
|
|
|
|
|
|
| 类型 | 命名格式 | 示例 |
|
|
|
|
|
|
|------|---------|------|
|
|
|
|
|
|
| Controller | `{Entity}Controller` | `DeviceController` |
|
|
|
|
|
|
| Service 接口 | `I{Entity}Service` | `IDeviceService` |
|
|
|
|
|
|
| Service 实现 | `{Entity}ServiceImpl` | `DeviceServiceImpl` |
|
|
|
|
|
|
| Domain 接口 | `I{Entity}Domain` | `IDeviceDomain` |
|
|
|
|
|
|
| Domain 实现 | `{Entity}DomainImpl` | `DeviceDomainImpl` |
|
|
|
|
|
|
| Mapper 接口 | `{Entity}Mapper` | `DeviceMapper` |
|
|
|
|
|
|
| Entity 实体 | `{Entity}Entity` | `DeviceEntity` |
|
|
|
|
|
|
| Domain Model | `{Entity}` | `Device` |
|
|
|
|
|
|
| Service DTO | `{Entity}DTO` | `DeviceDTO` |
|
|
|
|
|
|
| API VO | `{Entity}VO` | `DeviceVO` |
|
|
|
|
|
|
| Domain Convert | `{Entity}DomainConvert` | `DeviceDomainConvert` |
|
|
|
|
|
|
| Service Convert | `{Entity}ServiceConvert` | `DeviceServiceConvert` |
|
|
|
|
|
|
| Controller Convert | `{Entity}ControllerConvert` | `DeviceControllerConvert` |
|
|
|
|
|
|
| Remote Service | `Remote{Module}Service` | `RemoteDeviceService` |
|
|
|
|
|
|
| Fallback Factory | `Remote{Module}FallbackFactory` | `RemoteDeviceFallbackFactory` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 10.3 方法命名规范
|
|
|
|
|
|
|
|
|
|
|
|
| 操作类型 | 命名格式 | 示例 |
|
|
|
|
|
|
|---------|---------|------|
|
|
|
|
|
|
| 查询列表 | `select{Entity}List` | `selectDeviceList` |
|
|
|
|
|
|
| 查询单个 | `select{Entity}ById` | `selectDeviceById` |
|
|
|
|
|
|
| 新增 | `insert{Entity}` | `insertDevice` |
|
|
|
|
|
|
| 修改 | `update{Entity}` | `updateDevice` |
|
|
|
|
|
|
| 删除 | `delete{Entity}ById` | `deleteDeviceById` |
|
|
|
|
|
|
| 批量删除 | `delete{Entity}ByIds` | `deleteDeviceByIds` |
|
|
|
|
|
|
| 转换为Entity | `toEntity` | `toEntity` |
|
|
|
|
|
|
| 转换为Model | `toModel` | `toModel` |
|
|
|
|
|
|
| 转换为DTO | `toDTO` | `toDTO` |
|
|
|
|
|
|
| 转换为VO | `toVO` | `toVO` |
|
|
|
|
|
|
| 转换为List | `toModelList`, `toDTOList`, `toVOList` | `toModelList` |
|
|
|
|
|
|
|
|
|
|
|
|
### 10.4 字段命名规范
|
|
|
|
|
|
|
|
|
|
|
|
| 字段类型 | 命名格式 | 示例 |
|
|
|
|
|
|
|---------|---------|------|
|
|
|
|
|
|
| 主键ID | `{entity}_id` (数据库) <br> `{entity}Id` (Java) | `device_id` <br> `deviceId` |
|
|
|
|
|
|
| 普通字段 | `field_name` (数据库) <br> `fieldName` (Java) | `device_name` <br> `deviceName` |
|
|
|
|
|
|
| 外键字段 | `{ref_entity}_id` (数据库) <br> `{refEntity}Id` (Java) | `dock_id` <br> `dockId` |
|
|
|
|
|
|
| 布尔字段 | `is_{field}` (数据库) <br> `is{Field}` (Java) | `is_active` <br> `isActive` |
|
|
|
|
|
|
| 时间字段 | `{action}_time` (数据库) <br> `{action}Time` (Java) | `create_time` <br> `createTime` |
|
|
|
|
|
|
|
|
|
|
|
|
### 10.5 数据库命名规范
|
|
|
|
|
|
|
|
|
|
|
|
| 对象类型 | 命名格式 | 示例 |
|
|
|
|
|
|
|---------|---------|------|
|
|
|
|
|
|
| 表名 | `{module}_{table}` | `device_device`, `device_dock` |
|
|
|
|
|
|
| 主键 | `{table}_id` | `device_id`, `dock_id` |
|
|
|
|
|
|
| 普通索引 | `idx_{table}_{field}` | `idx_dock_device_id` |
|
|
|
|
|
|
| 唯一索引 | `uk_{table}_{field}` | `uk_device_sn` |
|
|
|
|
|
|
| 复合索引 | `idx_{table}_{field1}_{field2}` | `idx_dock_device_status` |
|
|
|
|
|
|
| Flyway历史表 | `flyway_{module}_schema_history` | `flyway_device_schema_history` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十一、对象转换流程
|
|
|
|
|
|
|
|
|
|
|
|
### 11.1 数据流转图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
前端请求
|
|
|
|
|
|
↓
|
|
|
|
|
|
Controller 接收 VO
|
|
|
|
|
|
↓ ControllerConvert.toDTO(vo)
|
|
|
|
|
|
Service 处理 DTO
|
|
|
|
|
|
↓ ServiceConvert.toModel(dto)
|
|
|
|
|
|
Domain 处理 Model
|
|
|
|
|
|
↓ DomainConvert.toEntity(model)
|
|
|
|
|
|
Mapper 操作 Entity
|
|
|
|
|
|
↓ 数据库
|
|
|
|
|
|
Database
|
|
|
|
|
|
↑
|
|
|
|
|
|
Mapper 返回 Entity
|
|
|
|
|
|
↑ DomainConvert.toModel(entity)
|
|
|
|
|
|
Domain 返回 Model
|
|
|
|
|
|
↑ ServiceConvert.toDTO(model)
|
|
|
|
|
|
Service 返回 DTO
|
|
|
|
|
|
↑ ControllerConvert.toVO(dto)
|
|
|
|
|
|
Controller 返回 VO
|
|
|
|
|
|
↑
|
|
|
|
|
|
前端响应
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 11.2 对象职责说明
|
|
|
|
|
|
|
|
|
|
|
|
| 对象类型 | 所属层 | 职责 | 特点 |
|
|
|
|
|
|
|---------|-------|------|------|
|
|
|
|
|
|
| **VO** (View Object) | API层 | 前后端数据交互 | 可包含展示逻辑字段 |
|
|
|
|
|
|
| **DTO** (Data Transfer Object) | Service层 | 业务逻辑编排 | 可包含业务组合字段 |
|
|
|
|
|
|
| **Model** | Domain层 | 领域业务逻辑 | 纯业务模型 |
|
|
|
|
|
|
| **Entity** | Mapper层 | 数据库映射 | 与数据库表一一对应 |
|
|
|
|
|
|
|
|
|
|
|
|
### 11.3 转换类职责
|
|
|
|
|
|
|
|
|
|
|
|
| 转换类 | 转换方向 | 位置 |
|
|
|
|
|
|
|-------|---------|------|
|
|
|
|
|
|
| **ControllerConvert** | VO ↔ DTO | `controller.convert` |
|
|
|
|
|
|
| **ServiceConvert** | DTO ↔ Model | `service.convert` |
|
|
|
|
|
|
| **DomainConvert** | Model ↔ Entity | `domain.convert` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十二、最佳实践
|
|
|
|
|
|
|
|
|
|
|
|
### 12.1 代码规范
|
|
|
|
|
|
|
|
|
|
|
|
1. **分层清晰**:严格遵守 Controller → Service → Domain → Mapper 分层
|
|
|
|
|
|
2. **单一职责**:每个类只负责一个职责
|
|
|
|
|
|
3. **依赖倒置**:依赖接口而非实现
|
|
|
|
|
|
4. **对象转换**:使用专门的 Convert 类进行对象转换
|
|
|
|
|
|
5. **异常处理**:使用统一的异常处理机制
|
|
|
|
|
|
6. **日志记录**:关键操作必须记录日志
|
|
|
|
|
|
7. **权限控制**:Controller 方法必须添加权限注解
|
|
|
|
|
|
|
|
|
|
|
|
### 12.2 数据库规范
|
|
|
|
|
|
|
|
|
|
|
|
1. **表名规范**:`{module}_{table}` 格式
|
|
|
|
|
|
2. **主键规范**:`{table}_id`,BIGINT,AUTO_INCREMENT
|
|
|
|
|
|
3. **BaseEntity字段**:所有表必须包含 create_by, create_time, update_by, update_time, remark
|
|
|
|
|
|
4. **索引规范**:外键字段必须添加索引
|
|
|
|
|
|
5. **字符集**:统一使用 utf8mb4
|
|
|
|
|
|
6. **存储引擎**:统一使用 InnoDB
|
|
|
|
|
|
7. **Flyway迁移**:所有数据库变更必须通过 Flyway 脚本
|
|
|
|
|
|
|
|
|
|
|
|
### 12.3 配置规范
|
|
|
|
|
|
|
|
|
|
|
|
1. **端口分配**:
|
|
|
|
|
|
- 基础服务:9200-9209
|
|
|
|
|
|
- 业务服务:9210-9299
|
|
|
|
|
|
- 监控服务:9100
|
|
|
|
|
|
- 特殊服务:9300+
|
|
|
|
|
|
|
|
|
|
|
|
2. **配置同步**:
|
|
|
|
|
|
- bootstrap.yml 的 server.port 必须与 docker-compose.yml 的容器端口一致
|
|
|
|
|
|
- Flyway 历史表名格式:`flyway_{module}_schema_history`
|
|
|
|
|
|
|
|
|
|
|
|
3. **服务命名**:
|
|
|
|
|
|
- 服务名称格式:`{module-name}`
|
|
|
|
|
|
- 在 ServiceNameConstants 中定义常量
|
|
|
|
|
|
|
|
|
|
|
|
### 12.4 API 规范
|
|
|
|
|
|
|
|
|
|
|
|
1. **Feign 客户端**:
|
|
|
|
|
|
- 接口命名:`Remote{Module}Service`
|
|
|
|
|
|
- contextId:`remote{Module}Service`
|
|
|
|
|
|
- 必须配置 fallbackFactory
|
|
|
|
|
|
|
|
|
|
|
|
2. **降级处理**:
|
|
|
|
|
|
- 工厂命名:`Remote{Module}FallbackFactory`
|
|
|
|
|
|
- 必须记录错误日志
|
|
|
|
|
|
- 返回友好的错误信息
|
|
|
|
|
|
|
|
|
|
|
|
3. **安全头**:
|
|
|
|
|
|
- 所有 Feign 方法必须包含 `@RequestHeader(SecurityConstants.FROM_SOURCE) String source`
|
|
|
|
|
|
|
|
|
|
|
|
### 12.5 Maven 规范
|
|
|
|
|
|
|
|
|
|
|
|
1. **新增服务步骤**:
|
|
|
|
|
|
- 在 ServiceNameConstants 中添加服务名常量
|
|
|
|
|
|
- 在根 pom.xml 的 dependencyManagement 中添加 API 依赖
|
|
|
|
|
|
- 在 ruoyi-api/pom.xml 中添加模块声明
|
|
|
|
|
|
- 创建业务模块和 API 模块
|
|
|
|
|
|
|
|
|
|
|
|
2. **依赖管理**:
|
|
|
|
|
|
- 版本号统一在根 pom.xml 管理
|
|
|
|
|
|
- 使用 `${ruoyi.version}` 引用版本
|
|
|
|
|
|
|
|
|
|
|
|
### 12.6 开发流程
|
|
|
|
|
|
|
|
|
|
|
|
1. **数据库设计** → 编写 Flyway 迁移脚本
|
|
|
|
|
|
2. **Mapper 层** → Entity + Mapper 接口 + Mapper XML
|
|
|
|
|
|
3. **Domain 层** → Model + Domain 接口 + Domain 实现 + Domain Convert
|
|
|
|
|
|
4. **Service 层** → DTO + Service 接口 + Service 实现 + Service Convert
|
|
|
|
|
|
5. **Controller 层** → Controller + Controller Convert
|
|
|
|
|
|
6. **API 层** → VO + Remote Service + Fallback Factory
|
|
|
|
|
|
7. **配置** → bootstrap.yml + docker-compose.yml
|
|
|
|
|
|
8. **测试** → 单元测试 + 集成测试
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 附录:快速参考
|
|
|
|
|
|
|
|
|
|
|
|
### A. 创建新模块检查清单
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] 数据库表设计(遵循命名规范)
|
|
|
|
|
|
- [ ] Flyway 迁移脚本(V{版本号}__{描述}.sql)
|
|
|
|
|
|
- [ ] Entity 实体类(继承 BaseEntity)
|
|
|
|
|
|
- [ ] Mapper 接口和 XML
|
|
|
|
|
|
- [ ] Domain Model(领域模型)
|
|
|
|
|
|
- [ ] Domain 接口和实现(构造器注入)
|
|
|
|
|
|
- [ ] Domain Convert(对象转换)
|
|
|
|
|
|
- [ ] Service DTO(数据传输对象)
|
|
|
|
|
|
- [ ] Service 接口和实现(字段注入)
|
|
|
|
|
|
- [ ] Service Convert(对象转换)
|
|
|
|
|
|
- [ ] Controller(权限控制、日志记录)
|
|
|
|
|
|
- [ ] Controller Convert(对象转换)
|
|
|
|
|
|
- [ ] API VO(视图对象)
|
|
|
|
|
|
- [ ] Remote Service(Feign 客户端)
|
|
|
|
|
|
- [ ] Fallback Factory(降级处理)
|
|
|
|
|
|
- [ ] ServiceNameConstants(服务名常量)
|
|
|
|
|
|
- [ ] 根 pom.xml(依赖管理)
|
|
|
|
|
|
- [ ] ruoyi-api/pom.xml(模块声明)
|
|
|
|
|
|
- [ ] bootstrap.yml(端口配置)
|
|
|
|
|
|
- [ ] docker-compose.yml(容器配置)
|
|
|
|
|
|
|
|
|
|
|
|
### B. 常见问题
|
|
|
|
|
|
|
|
|
|
|
|
**Q: 为什么 Domain 层使用构造器注入,Service 层使用字段注入?**
|
|
|
|
|
|
A: Domain 层使用构造器注入是为了保证依赖不可变和更好的可测试性;Service 层使用字段注入是为了代码简洁和符合项目现有风格。
|
|
|
|
|
|
|
|
|
|
|
|
**Q: 为什么需要这么多对象(VO、DTO、Model、Entity)?**
|
|
|
|
|
|
A: 不同层次的对象有不同的职责,这样可以实现关注点分离,提高代码的可维护性和可扩展性。
|
|
|
|
|
|
|
|
|
|
|
|
**Q: Convert 类为什么使用静态方法?**
|
|
|
|
|
|
A: Convert 类是无状态的工具类,使用静态方法可以避免不必要的对象创建,提高性能。
|
|
|
|
|
|
|
|
|
|
|
|
**Q: 如何处理端口冲突?**
|
|
|
|
|
|
A: 按照端口分配规范,基础服务使用 9200-9209,业务服务使用 9210-9299,确保 bootstrap.yml 和 docker-compose.yml 中的端口配置一致。
|
|
|
|
|
|
|
|
|
|
|
|
**Q: Flyway 迁移脚本命名有什么要求?**
|
|
|
|
|
|
A: 格式为 `V{版本号}__{描述}.sql`,版本号从1开始递增不能跳号,描述使用下划线分隔的英文,首字母大写。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**文档结束**
|
|
|
|
|
|
|
|
|
|
|
|
> 本文档将持续更新,如有疑问或建议,请联系架构团队。
|