# RuoYi-Vue 开发指南
## 1. 开发环境准备
### 1.1 环境要求
#### 基础环境要求
| 组件 | 版本要求 | 推荐版本 | 说明 |
|------|----------|----------|------|
| **JDK** | 1.8+ | JDK 1.8.0_202 | 确保JAVA_HOME配置正确 |
| **Maven** | 3.6+ | Maven 3.6.3 | 依赖管理和项目构建 |
| **MySQL** | 5.7+ | MySQL 8.0.28 | 数据库服务 |
| **Redis** | 6.0+ | Redis 6.2.7 | 缓存服务 |
| **Node.js** | 14+ | Node.js 16.17.0 | 前端开发环境 |
| **npm** | 6.14+ | npm 8.15.0 | 前端包管理器 |
#### IDE推荐配置
**后端开发IDE:**
- ✅ **IntelliJ IDEA** (推荐)
- 安装插件:Lombok、MyBatis Log Plugin、Maven Helper
- ✅ **Eclipse** + Spring Tool Suite
- ✅ **Visual Studio Code** + Java Extension Pack
**前端开发IDE:**
- ✅ **Visual Studio Code** (推荐)
- 安装插件:Vetur、ESLint、Prettier、Auto Rename Tag
- ✅ **WebStorm**
### 1.2 环境安装指南
#### JDK安装配置
```bash
# 1. 下载JDK 1.8
# https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
# 2. 配置环境变量 (Windows)
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_202
set PATH=%JAVA_HOME%\bin;%PATH%
# 3. 配置环境变量 (macOS/Linux)
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
# 4. 验证安装
java -version
javac -version
```
#### MySQL安装配置
```bash
# 1. 安装MySQL (使用Docker,推荐)
docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_DATABASE=ry-vue \
mysql:8.0
# 2. 创建数据库
mysql -u root -p
CREATE DATABASE `ry-vue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
# 3. 导入初始化脚本
mysql -u root -p ry-vue < sql/ry_20210908.sql
mysql -u root -p ry-vue < sql/quartz.sql
```
#### Redis安装配置
```bash
# 1. 安装Redis (使用Docker,推荐)
docker run -d \
--name redis \
-p 6379:6379 \
redis:6.2-alpine
# 2. 本地安装 (macOS)
brew install redis
brew services start redis
# 3. 本地安装 (CentOS/RHEL)
yum install redis
systemctl start redis
systemctl enable redis
# 4. 验证安装
redis-cli ping
# 应该返回 PONG
```
#### Node.js安装
```bash
# 1. 使用NVM安装 (推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 16.17.0
nvm use 16.17.0
# 2. 直接下载安装
# https://nodejs.org/
# 3. 验证安装
node -v
npm -v
# 4. 配置国内镜像源 (可选)
npm config set registry https://registry.npmmirror.com/
```
## 2. 项目快速启动
### 2.1 项目获取与配置
#### 项目下载
```bash
# 1. 克隆项目 (如果是Git仓库)
git clone https://gitee.com/y_project/RuoYi-Vue.git
cd RuoYi-Vue
# 2. 或者直接下载解压项目压缩包
```
#### 数据库配置
```yaml
# ruoyi-admin/src/main/resources/application-druid.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: password
# 从库数据源 (可选)
slave:
enabled: false
url:
username:
password:
```
#### Redis配置
```yaml
# ruoyi-admin/src/main/resources/application.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 10s
lettuce:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms
```
### 2.2 后端启动
```bash
# 1. 进入项目根目录
cd RuoYi-Vue-master
# 2. Maven清理和编译
mvn clean install -Dmaven.test.skip=true
# 3. 启动应用 (方式一:Maven命令)
cd ruoyi-admin
mvn spring-boot:run
# 4. 启动应用 (方式二:java命令)
cd ruoyi-admin/target
java -jar ruoyi-admin.jar
# 5. 启动应用 (方式三:脚本启动)
./ry.sh start
# 6. 查看启动日志
tail -f logs/sys-info.log
```
#### 启动验证
```bash
# 检查应用是否启动成功
curl http://localhost:8080/captchaImage
# 检查健康状态
curl http://localhost:8080/actuator/health
# 查看端口占用
netstat -an | grep 8080
```
### 2.3 前端启动
```bash
# 1. 进入前端项目目录
cd ruoyi-ui
# 2. 安装依赖
npm install
# 3. 启动开发服务器
npm run dev
# 4. 构建生产版本
npm run build:prod
# 5. 预览构建结果
npm run preview
```
#### 前端配置文件
```javascript
// ruoyi-ui/.env.development
# 开发环境配置
ENV = 'development'
# 若依管理系统/开发环境
VUE_APP_TITLE = 若依管理系统
# 开发环境配置
VUE_APP_ENV = 'development'
# 若依管理系统/开发环境
VUE_APP_BASE_API = '/dev-api'
```
### 2.4 访问系统
#### 系统访问地址
| 服务 | 访问地址 | 默认账号 |
|------|----------|----------|
| **前端系统** | http://localhost | admin / admin123 |
| **后端接口** | http://localhost:8080 | - |
| **接口文档** | http://localhost:8080/swagger-ui.html | - |
| **Druid监控** | http://localhost:8080/druid | admin / 123456 |
#### 默认管理员账号
```
用户名:admin
密码:admin123
```
## 3. 开发规范与约定
### 3.1 代码规范
#### Java代码规范
**1. 命名规范**
```java
// 类名:大驼峰命名法
public class SysUserController {
// 常量:全大写,下划线分隔
private static final String USER_CACHE_KEY = "user:cache:";
// 变量:小驼峰命名法
private String userName;
// 方法:小驼峰命名法,动词开头
public AjaxResult getUserList() {
return success();
}
}
```
**2. 注释规范**
```java
/**
* 用户信息
*
* @author ruoyi
* @date 2024-01-01
*/
@Entity
@Table(name = "sys_user")
public class SysUser extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 用户ID */
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
/** 用户账号 */
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
private String userName;
/**
* 获取用户信息
*
* @param userId 用户ID
* @return 用户信息
*/
public SysUser getUserById(Long userId) {
// 实现逻辑
return null;
}
}
```
**3. 异常处理规范**
```java
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) {
log.error(e.getMessage(), e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 权限校验异常
*/
@ExceptionHandler(AccessDeniedException.class)
public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
}
}
```
#### JavaScript代码规范
**1. 命名规范**
```javascript
// 常量:全大写,下划线分隔
const API_BASE_URL = '/dev-api';
// 变量:小驼峰命名法
const userName = 'admin';
// 函数:小驼峰命名法,动词开头
function getUserList() {
return request({
url: '/system/user/list',
method: 'get'
});
}
// 组件名:大驼峰命名法
export default {
name: 'UserList',
components: {
UserDialog
}
};
```
**2. Vue组件规范**
```vue
```
### 3.2 数据库设计规范
#### 表设计规范
**1. 表命名规范**
```sql
-- 系统表前缀:sys_
CREATE TABLE `sys_user` (
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB COMMENT='用户信息表';
-- 业务表前缀:根据模块命名
CREATE TABLE `biz_order` (
`order_id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB COMMENT='订单信息表';
```
**2. 字段设计规范**
```sql
-- 标准字段设计
CREATE TABLE `sys_user` (
-- 主键:bigint,自增
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-- 业务字段:合适的数据类型和长度
`user_name` varchar(30) NOT NULL COMMENT '用户账号',
`nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
`email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
`phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码',
`sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
`status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
-- 审计字段:每张表必须包含
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
-- 主键和索引
PRIMARY KEY (`user_id`),
UNIQUE KEY `uk_user_name` (`user_name`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=100 COMMENT='用户信息表';
```
**3. 索引设计规范**
```sql
-- 主键索引:每张表必须有主键
PRIMARY KEY (`id`)
-- 唯一索引:业务唯一字段
UNIQUE KEY `uk_user_name` (`user_name`)
-- 普通索引:经常查询的字段
KEY `idx_status` (`status`)
KEY `idx_create_time` (`create_time`)
-- 复合索引:多字段组合查询
KEY `idx_dept_status` (`dept_id`, `status`)
-- 外键索引:关联字段
KEY `idx_dept_id` (`dept_id`)
```
### 3.3 API设计规范
#### RESTful API设计
**1. URL设计规范**
```java
// 资源型URL,使用名词复数形式
@RequestMapping("/system/users")
@RestController
public class SysUserController {
// GET /system/users - 获取用户列表
@GetMapping
public TableDataInfo list(SysUser user) {
startPage();
List list = userService.selectUserList(user);
return getDataTable(list);
}
// GET /system/users/{id} - 获取单个用户
@GetMapping("/{userId}")
public AjaxResult getInfo(@PathVariable("userId") Long userId) {
return AjaxResult.success(userService.selectUserById(userId));
}
// POST /system/users - 新增用户
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
return toAjax(userService.insertUser(user));
}
// PUT /system/users/{id} - 更新用户
@PutMapping("/{userId}")
public AjaxResult edit(@Validated @RequestBody SysUser user) {
return toAjax(userService.updateUser(user));
}
// DELETE /system/users/{id} - 删除用户
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds) {
return toAjax(userService.deleteUserByIds(userIds));
}
}
```
**2. 响应格式规范**
```java
// 统一响应格式
public class AjaxResult extends HashMap {
private static final long serialVersionUID = 1L;
/** 状态码 */
public static final String CODE_TAG = "code";
/** 返回内容 */
public static final String MSG_TAG = "msg";
/** 数据对象 */
public static final String DATA_TAG = "data";
/**
* 状态类型
*/
public enum Type {
/** 成功 */
SUCCESS(200),
/** 警告 */
WARN(301),
/** 错误 */
ERROR(500);
private final int value;
}
}
// 成功响应示例
{
"code": 200,
"msg": "操作成功",
"data": {
"userId": 1,
"userName": "admin",
"nickName": "管理员"
}
}
// 分页响应示例
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"userId": 1,
"userName": "admin"
}
],
"total": 100
}
// 错误响应示例
{
"code": 500,
"msg": "用户名不能为空"
}
```
## 4. 开发最佳实践
### 4.1 后端开发实践
#### 分层架构实践
**1. Controller层最佳实践**
```java
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
@Autowired
private ISysUserService userService;
/**
* 获取用户列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage(); // 开启分页
List list = userService.selectUserList(user);
return getDataTable(list); // 返回分页结果
}
/**
* 新增用户
*/
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
// 1. 参数校验
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) {
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
// 2. 设置默认值
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
// 3. 调用服务层
return toAjax(userService.insertUser(user));
}
}
```
**2. Service层最佳实践**
```java
@Service
public class SysUserServiceImpl implements ISysUserService {
@Autowired
private SysUserMapper userMapper;
@Autowired
private SysUserRoleMapper userRoleMapper;
/**
* 新增用户信息
*/
@Override
@Transactional
public int insertUser(SysUser user) {
// 1. 新增用户信息
int rows = userMapper.insertUser(user);
// 2. 新增用户与角色管理
insertUserRole(user);
return rows;
}
/**
* 新增用户角色信息
*/
public void insertUserRole(SysUser user) {
Long[] roles = user.getRoleIds();
if (StringUtils.isNotNull(roles)) {
// 新增用户与角色管理
List list = new ArrayList();
for (Long roleId : roles) {
SysUserRole ur = new SysUserRole();
ur.setUserId(user.getUserId());
ur.setRoleId(roleId);
list.add(ur);
}
if (list.size() > 0) {
userRoleMapper.batchUserRole(list);
}
}
}
}
```
**3. Mapper层最佳实践**
```java
@Mapper
public interface SysUserMapper {
/**
* 根据条件分页查询用户列表
*
* @param sysUser 用户信息
* @return 用户信息集合信息
*/
public List selectUserList(SysUser sysUser);
/**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
public int insertUser(SysUser user);
}
```
```xml
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on r.role_id = ur.role_id
insert into sys_user(
user_id,
dept_id,
user_name,
nick_name,
email,
avatar,
phonenumber,
sex,
password,
status,
create_by,
remark,
create_time
)values(
#{userId},
#{deptId},
#{userName},
#{nickName},
#{email},
#{avatar},
#{phonenumber},
#{sex},
#{password},
#{status},
#{createBy},
#{remark},
sysdate()
)
```
#### 缓存使用实践
```java
@Service
public class SysDictDataServiceImpl implements ISysDictDataService {
@Autowired
private RedisCache redisCache;
@Autowired
private SysDictDataMapper dictDataMapper;
/**
* 根据字典类型查询字典数据
*/
@Override
public List selectDictDataByType(String dictType) {
String cacheKey = getCacheKey(dictType);
List dictDatas = redisCache.getCacheObject(cacheKey);
if (StringUtils.isNotEmpty(dictDatas)) {
return dictDatas;
}
dictDatas = dictDataMapper.selectDictDataByType(dictType);
if (StringUtils.isNotEmpty(dictDatas)) {
redisCache.setCacheObject(cacheKey, dictDatas, 30, TimeUnit.MINUTES);
}
return dictDatas;
}
/**
* 更新字典数据
*/
@Override
public int updateDictData(SysDictData dictData) {
int row = dictDataMapper.updateDictData(dictData);
if (row > 0) {
// 清除相关缓存
redisCache.deleteObject(getCacheKey(dictData.getDictType()));
}
return row;
}
private String getCacheKey(String dictType) {
return CacheConstants.SYS_DICT_KEY + dictType;
}
}
```
### 4.2 前端开发实践
#### Vue组件开发实践
**1. 页面组件结构**
```vue
```
**2. API调用封装**
```javascript
// api/system/user.js
import request from '@/utils/request'
// 查询用户列表
export function listUser(query) {
return request({
url: '/system/user/list',
method: 'get',
params: query
})
}
// 查询用户详细
export function getUser(userId) {
return request({
url: '/system/user/' + userId,
method: 'get'
})
}
// 新增用户
export function addUser(data) {
return request({
url: '/system/user',
method: 'post',
data: data
})
}
// 修改用户
export function updateUser(data) {
return request({
url: '/system/user',
method: 'put',
data: data
})
}
// 删除用户
export function delUser(userId) {
return request({
url: '/system/user/' + userId,
method: 'delete'
})
}
```
**3. 工具函数封装**
```javascript
// utils/index.js
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
} else {
result += part + encodeURIComponent(value) + "&";
}
}
}
return result
}
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
};
var childrenListMap = {};
var nodeIds = {};
var tree = [];
for (let d of data) {
let parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (let d of data) {
let parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (let t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
return tree;
}
```
## 5. 代码生成器使用指南
### 5.1 使用流程
#### Step 1: 创建数据库表
```sql
-- 创建示例表
DROP TABLE IF EXISTS `test_demo`;
CREATE TABLE `test_demo` (
`demo_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`demo_name` varchar(30) DEFAULT '' COMMENT '名称',
`demo_type` char(1) DEFAULT '0' COMMENT '类型(0正常 1停用)',
`demo_status` char(1) DEFAULT '0' COMMENT '状态(0正常 1删除)',
`demo_content` longtext COMMENT '内容',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`demo_id`)
) ENGINE=InnoDB COMMENT = '测试演示表';
```
#### Step 2: 导入表到代码生成
1. 登录系统,进入 `系统工具 -> 代码生成`
2. 点击 `导入` 按钮,选择需要生成代码的表
3. 点击 `导入` 确认
#### Step 3: 配置生成信息
```java
// 基本信息配置
表名称: test_demo
表描述: 测试演示表
实体类名称: TestDemo
作者: ruoyi
生成包路径: com.ruoyi.system
生成模块名: system
生成业务名: demo
生成功能名: 测试演示
上级菜单: 系统工具
```
#### Step 4: 字段信息配置
| 字段名 | 字段描述 | 字段类型 | Java类型 | Java字段 | 是否必填 | 是否为插入字段 | 是否为编辑字段 | 是否为列表字段 | 是否为查询字段 | 查询方式 | 显示类型 |
|--------|----------|----------|----------|----------|----------|---------------|---------------|---------------|---------------|----------|----------|
| demo_id | 主键 | bigint | Long | demoId | 否 | 否 | 否 | 否 | 否 | = | 文本框 |
| demo_name | 名称 | varchar(30) | String | demoName | 是 | 是 | 是 | 是 | 是 | LIKE | 文本框 |
| demo_type | 类型 | char(1) | String | demoType | 否 | 是 | 是 | 是 | 是 | = | 选择框 |
| demo_status | 状态 | char(1) | String | demoStatus | 否 | 是 | 是 | 是 | 是 | = | 单选框 |
| demo_content | 内容 | longtext | String | demoContent | 否 | 是 | 是 | 否 | 否 | = | 文本域 |
#### Step 5: 生成代码
1. 配置完成后,点击 `生成代码` 按钮
2. 下载生成的代码包
3. 解压并导入到项目中
### 5.2 生成的代码结构
```
ruoyi-system/
├── domain/
│ └── TestDemo.java # 实体类
├── mapper/
│ ├── TestDemoMapper.java # Mapper接口
│ └── TestDemoMapper.xml # Mapper XML
├── service/
│ ├── ITestDemoService.java # Service接口
│ └── impl/
│ └── TestDemoServiceImpl.java # Service实现
└── controller/
└── TestDemoController.java # Controller
ruoyi-ui/src/
├── api/system/
│ └── demo.js # API接口
└── views/system/demo/
└── index.vue # 页面组件
```
### 5.3 生成后的集成步骤
#### Step 1: 后端集成
```java
// 1. 确认文件已正确放置在对应目录
// 2. 无需额外配置,Spring Boot会自动扫描
// 3. 如果使用了自定义配置,需要检查包扫描路径
@SpringBootApplication
@ComponentScan(basePackages = {"com.ruoyi"})
public class RuoYiApplication {
public static void main(String[] args) {
SpringApplication.run(RuoYiApplication.class, args);
}
}
```
#### Step 2: 前端集成
```javascript
// 1. 将生成的 demo.js 放置到 src/api/system/ 目录
// 2. 将生成的 index.vue 放置到 src/views/system/demo/ 目录
// 3. 在路由中添加新的路由配置(如果需要)
// router/index.js
{
path: '/system/demo',
component: Layout,
hidden: true,
children: [
{
path: 'index',
component: () => import('@/views/system/demo/index'),
name: 'Demo',
meta: { title: '测试演示', icon: '' }
}
]
}
```
#### Step 3: 菜单配置
1. 登录系统,进入 `系统管理 -> 菜单管理`
2. 新增菜单项:
```
菜单名称: 测试演示
上级菜单: 系统工具
菜单类型: 菜单
路由地址: demo
组件路径: system/demo/index
权限字符: system:demo:list
```
3. 新增按钮权限:
```
新增: system:demo:add
修改: system:demo:edit
删除: system:demo:remove
导出: system:demo:export
```
## 6. 部署指南
### 6.1 开发环境部署
#### 本地开发部署
```bash
# 1. 启动MySQL和Redis服务
docker-compose up -d mysql redis
# 2. 导入数据库脚本
mysql -u root -p ry-vue < sql/ry_20210908.sql
# 3. 启动后端服务
cd ruoyi-admin
mvn spring-boot:run
# 4. 启动前端服务
cd ruoyi-ui
npm install
npm run dev
# 5. 访问系统
# 前端: http://localhost
# 后端: http://localhost:8080
```
### 6.2 生产环境部署
#### 传统部署方式
**1. 后端部署**
```bash
# 1. Maven打包
mvn clean package -Dmaven.test.skip=true
# 2. 上传jar包到服务器
scp ruoyi-admin/target/ruoyi-admin.jar user@server:/opt/ruoyi/
# 3. 创建启动脚本
cat > /opt/ruoyi/start.sh << 'EOF'
#!/bin/bash
export JAVA_OPTS="-Xms512m -Xmx2048m -Xss256k"
export SERVER_PORT=8080
export SPRING_PROFILES_ACTIVE=prod
nohup java $JAVA_OPTS -jar ruoyi-admin.jar \
--server.port=$SERVER_PORT \
--spring.profiles.active=$SPRING_PROFILES_ACTIVE \
> logs/application.log 2>&1 &
echo $! > ruoyi.pid
echo "RuoYi started with PID: $(cat ruoyi.pid)"
EOF
chmod +x start.sh
# 4. 启动服务
./start.sh
```
**2. 前端部署**
```bash
# 1. 构建生产版本
npm run build:prod
# 2. 上传到Web服务器
scp -r dist/ user@server:/var/www/html/ruoyi/
# 3. 配置Nginx
cat > /etc/nginx/conf.d/ruoyi.conf << 'EOF'
server {
listen 80;
server_name your-domain.com;
location / {
root /var/www/html/ruoyi;
index index.html;
try_files $uri $uri/ /index.html;
}
location /prod-api/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
# 4. 重启Nginx
nginx -s reload
```
#### Docker容器化部署
**1. 创建Dockerfile**
```dockerfile
# 后端Dockerfile
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY ruoyi-admin/target/ruoyi-admin.jar app.jar
ENV JAVA_OPTS="-Xms512m -Xmx1024m"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
```
```dockerfile
# 前端Dockerfile
FROM nginx:alpine
COPY dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
**2. 创建docker-compose.yml**
```yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: ruoyi-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: ry-vue
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./sql:/docker-entrypoint-initdb.d
redis:
image: redis:6.2-alpine
container_name: ruoyi-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
ruoyi-backend:
build: .
container_name: ruoyi-backend
restart: always
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
SPRING_REDIS_HOST: redis
depends_on:
- mysql
- redis
ruoyi-frontend:
build:
context: .
dockerfile: Dockerfile.frontend
container_name: ruoyi-frontend
restart: always
ports:
- "80:80"
depends_on:
- ruoyi-backend
volumes:
mysql_data:
redis_data:
```
**3. 部署命令**
```bash
# 1. 构建并启动所有服务
docker-compose up -d
# 2. 查看服务状态
docker-compose ps
# 3. 查看日志
docker-compose logs -f ruoyi-backend
# 4. 停止服务
docker-compose down
```
### 6.3 监控和运维
#### 应用监控配置
```yaml
# application-prod.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
```
#### 日志配置
```xml
logs/application.log
logs/application.%d{yyyy-MM-dd}.log
30
1GB
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n
```
## 7. 常见问题与解决方案
### 7.1 环境问题
**Q: Maven依赖下载失败**
```bash
# A: 配置国内镜像源
# ~/.m2/settings.xml
aliyun
central
Aliyun Central
https://maven.aliyun.com/repository/central
```
**Q: NPM包安装失败**
```bash
# A: 使用国内镜像
npm config set registry https://registry.npmmirror.com/
# 或使用yarn
yarn config set registry https://registry.npmmirror.com/
```
**Q: MySQL连接失败**
```yaml
# A: 检查数据库配置和权限
spring:
datasource:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
```
### 7.2 开发问题
**Q: 跨域问题**
```java
// A: 后端跨域配置
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setMaxAge(1800L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
```
**Q: 权限验证失败**
```java
// A: 检查权限配置和用户角色
@PreAuthorize("@ss.hasPermi('system:user:list')")
// 确保用户拥有对应权限
```
### 7.3 部署问题
**Q: 前端页面空白**
```javascript
// A: 检查publicPath配置
// vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
}
```
**Q: 后端接口404**
```yaml
# A: 检查context-path配置
server:
servlet:
context-path: /
```
## 8. 总结
本开发指南提供了RuoYi-Vue项目从环境搭建到生产部署的完整开发流程,涵盖了:
✅ **环境准备**:详细的开发环境安装和配置指南
✅ **快速启动**:项目快速启动和验证步骤
✅ **开发规范**:代码规范和API设计最佳实践
✅ **开发实践**:分层架构和组件开发最佳实践
✅ **代码生成**:代码生成器的使用方法和集成步骤
✅ **部署指南**:从开发环境到生产环境的部署方案
✅ **问题解决**:常见问题的解决方案
通过遵循本指南,开发者可以:
- 快速搭建开发环境并启动项目
- 掌握项目的开发规范和最佳实践
- 高效使用代码生成器提升开发效率
- 顺利完成项目的部署和上线
- 解决开发过程中的常见问题
RuoYi-Vue作为一个成熟的企业级开发框架,为快速构建高质量的管理系统提供了坚实的基础。结合本开发指南,开发团队能够快速上手并充分发挥框架的优势,提升项目开发效率和质量。