第15章 - 最佳实践与常见问题
第15章 - 最佳实践与常见问题
15.1 开发最佳实践
15.1.1 代码规范
命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 类名 | 大驼峰 | SysUserController |
| 方法名 | 小驼峰 | getUserInfo |
| 变量名 | 小驼峰 | userName |
| 常量名 | 全大写下划线 | MAX_PAGE_SIZE |
| 包名 | 全小写 | com.ruoyi.system |
| 数据库表名 | 小写下划线 | sys_user |
| 数据库字段 | 小写下划线 | user_name |
Controller层规范
@RestController
@RequestMapping("/user")
public class SysUserController extends BaseController {
@Autowired
private ISysUserService userService;
/**
* 查询用户列表
* - 使用GET请求查询
* - 使用@RequiresPermissions注解控制权限
* - 返回TableDataInfo用于分页
*/
@RequiresPermissions("system:user:list")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage(); // 开启分页
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
/**
* 新增用户
* - 使用POST请求新增
* - 使用@Log注解记录操作日志
* - 使用@RequestBody接收JSON数据
*/
@RequiresPermissions("system:user:add")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
if (!userService.checkUserNameUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
user.setCreateBy(SecurityUtils.getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
* 修改用户
* - 使用PUT请求修改
*/
@RequiresPermissions("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user) {
if (!userService.checkUserNameUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
user.setUpdateBy(SecurityUtils.getUsername());
return toAjax(userService.updateUser(user));
}
/**
* 删除用户
* - 使用DELETE请求删除
* - 支持批量删除
*/
@RequiresPermissions("system:user:remove")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds) {
if (ArrayUtils.contains(userIds, SecurityUtils.getUserId())) {
return error("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
}
Service层规范
@Service
public class SysUserServiceImpl implements ISysUserService {
@Autowired
private SysUserMapper userMapper;
@Autowired
private SysRoleMapper roleMapper;
/**
* 1. 使用@Transactional管理事务
* 2. 复杂业务使用事务注解
* 3. 只读操作可以不加事务
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertUser(SysUser user) {
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户与角色关联
insertUserRole(user);
return rows;
}
/**
* 业务校验放在Service层
*/
@Override
public boolean checkUserNameUnique(SysUser user) {
Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
SysUser info = userMapper.checkUserNameUnique(user.getUserName());
if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
return false;
}
return true;
}
}
15.1.2 安全规范
SQL注入防护
// 错误示例 - 存在SQL注入风险
@Select("SELECT * FROM sys_user WHERE user_name = '" + userName + "'")
SysUser selectByUserName(String userName);
// 正确示例 - 使用参数化查询
@Select("SELECT * FROM sys_user WHERE user_name = #{userName}")
SysUser selectByUserName(@Param("userName") String userName);
XSS防护
// 在Controller中使用@Validated验证输入
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
// ...
}
// 在实体类中使用@Xss注解
public class SysUser {
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
private String nickName;
}
敏感数据处理
// 密码加密存储
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
// 敏感字段脱敏返回
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
// 或使用注解脱敏
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String phone;
15.1.3 性能优化
数据库优化
// 1. 只查询需要的字段
<select id="selectUserList" resultMap="SysUserResult">
select u.user_id, u.user_name, u.nick_name, u.status
from sys_user u
</select>
// 2. 分页查询
startPage(); // 开启分页
List<SysUser> list = userService.selectUserList(user);
// 3. 合理使用索引
CREATE INDEX idx_user_name ON sys_user(user_name);
CREATE INDEX idx_status ON sys_user(status);
// 4. 避免N+1查询问题,使用关联查询
<select id="selectUserWithRole" resultMap="SysUserResult">
select u.*, r.role_name
from sys_user u
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on ur.role_id = r.role_id
</select>
缓存优化
// 使用Redis缓存热点数据
@Autowired
private RedisCache redisCache;
public SysUser selectUserByUserName(String userName) {
String cacheKey = CacheConstants.SYS_USER_KEY + userName;
SysUser user = redisCache.getCacheObject(cacheKey);
if (user == null) {
user = userMapper.selectUserByUserName(userName);
redisCache.setCacheObject(cacheKey, user, 30, TimeUnit.MINUTES);
}
return user;
}
// 更新时删除缓存
public int updateUser(SysUser user) {
int rows = userMapper.updateUser(user);
if (rows > 0) {
redisCache.deleteObject(CacheConstants.SYS_USER_KEY + user.getUserName());
}
return rows;
}
接口优化
// 1. 异步处理耗时操作
@Async
public void sendEmail(String email, String content) {
// 发送邮件
}
// 2. 批量操作代替循环单条操作
// 错误示例
for (SysUser user : userList) {
userMapper.insertUser(user);
}
// 正确示例
userMapper.batchInsertUser(userList);
// 3. 使用线程池处理并发请求
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
15.2 常见问题解答
15.2.1 启动问题
Q1: 服务启动失败,提示Nacos连接失败
解决方案:
1. 确保Nacos服务已启动
2. 检查bootstrap.yml中Nacos地址配置
3. 检查防火墙是否开放8848端口
4. 确认Nacos中已导入相关配置文件
Q2: 服务启动后无法访问
解决方案:
1. 检查网关服务是否正常启动
2. 确认网关路由配置正确
3. 检查服务是否成功注册到Nacos
4. 查看服务日志是否有报错
Q3: 数据库连接失败
解决方案:
1. 检查MySQL服务是否启动
2. 确认数据库连接参数正确
3. 检查数据库用户权限
4. 确认防火墙开放3306端口
15.2.2 认证授权问题
Q4: 登录提示验证码错误
解决方案:
1. 确保Redis服务正常运行
2. 检查验证码是否过期
3. 清除浏览器缓存重新获取验证码
4. 检查Redis中是否存储了验证码
Q5: Token过期时间如何配置
# Nacos配置中心修改
security:
# token过期时间(默认30分钟)
expireTime: 30
Q6: 如何实现单点登录
1. 在认证中心记录用户Token
2. 新登录时使旧Token失效
3. 使用Redis存储Token与用户关系
4. 登录时检查并踢出旧会话
15.2.3 权限问题
Q7: 按钮权限不生效
解决方案:
1. 确认用户已分配对应角色
2. 确认角色已分配对应菜单权限
3. 检查权限标识是否正确
4. 清除前端缓存重新登录
Q8: 数据权限如何配置
// 1. 在Mapper.xml中使用数据权限过滤
<select id="selectUserList" resultMap="SysUserResult">
select u.* from sys_user u
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
// 2. 在Service中添加数据权限注解
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user) {
return userMapper.selectUserList(user);
}
15.2.4 前端问题
Q9: 前端访问后端接口跨域
// vue.config.js配置代理
devServer: {
proxy: {
'/dev-api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/dev-api': ''
}
}
}
}
Q10: 菜单图标不显示
解决方案:
1. 确认图标名称正确
2. 使用Element Plus内置图标或SVG图标
3. 自定义图标需放在src/assets/icons/svg目录
4. 重新编译前端项目
15.2.5 部署问题
Q11: Docker部署MySQL数据丢失
# 使用数据卷持久化
volumes:
- ./mysql/data:/var/lib/mysql
Q12: Nginx配置后刷新404
# 配置try_files
location / {
root /home/ruoyi/projects/ruoyi-ui;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
Q13: 生产环境如何配置
# 生产环境建议配置
spring:
profiles:
active: prod
datasource:
dynamic:
datasource:
master:
# 使用连接池
druid:
initial-size: 10
min-idle: 10
max-active: 100
max-wait: 60000
# 日志级别调整
logging:
level:
com.ruoyi: warn
15.3 版本升级指南
15.3.1 升级步骤
1. 备份当前项目代码和数据库
2. 下载最新版本代码
3. 对比pom.xml依赖变化
4. 对比配置文件变化
5. 执行数据库升级脚本
6. 测试各功能模块
7. 部署上线
15.3.2 版本变更日志
关注官方发布说明:
- GitHub Releases
- 官方文档更新日志
- QQ群公告
15.4 总结
本章介绍了RuoYi-Cloud开发中的最佳实践和常见问题解决方案,包括:
- 代码规范:命名规范、Controller/Service层规范
- 安全规范:SQL注入、XSS防护、敏感数据处理
- 性能优化:数据库优化、缓存优化、接口优化
- 常见问题:启动、认证、权限、前端、部署问题
- 版本升级:升级步骤和注意事项
遵循这些最佳实践可以提高代码质量和系统稳定性,解决开发中遇到的常见问题。

浙公网安备 33010602011771号