[TOC]
# 第1章 项目搭建
后台管理系统演示地址:http://ry-spzx-admin.atguigu.cn/
移动端演示地址:http://ry-spzx.atguigu.cn/
## 1 项目简介
`尚品甄选`是基于`若依微服务版本`开发的一个电商系统,项目包含平台管理端与手机H5端。有一套完善的电商业务流程:包含商品管理、商品详情、单点登录、购物车、订单、支付、库存管理等,其中覆盖了分布式文件系统、缓存、消息队列等多种业务场景和技术实现。
### 1.1 为什么讲电商
电商网站有很多典型的特征:
- 访问量大
- 数据量大
- 有一定的业务复杂性
### 1.2 电商的主要模式
**B2B(Business to Business)**
指进行交易的供需双方都是商家(或企业、公司)。
案例:阿里巴巴1688
**C2C(Consumer to Customer)**
或Customer to Customer,意思就是消费者个人间的电子商务行为。
案例:淘宝、闲鱼
**B2C(Business-to-Customer)**
简称“商对客”,商家直接面向消费者销售产品和服务的模式。
案例:京东自营
**B2B2C(Business to Business to Consumer)**
第一个B指服务供应商,为商家提供销售平台,第二个B指商家,C表示消费者。
案例:天猫商城
**C2B(Consumer to Business)**
消费者到商家,先有消费者需求产生,后有商家提供商品或服务。
案例:猪八戒
**O2O(Online To Offline)**
线上到线下,指线上下单线下服务。
案例:美团、饿了么
### 1.3 核心技术
- SpringBoot:简化Spring应用的初始搭建以及开发过程
- SpringCloud:Spring Cloud Gateway、Spring Cloud OpenFeign、Spring Cloud LoadBalancer、Spring Cloud Alibaba Nacos、Spring Cloud Alibaba Sentinel、Spring Cloud Task等
- MyBatis-Plus:持久层框架,依赖MyBatis
- **Redisson**:操作redis的框架
- ThreadPoolExecutor:使用线程池实现异步操作,提高效率
- Lombok:生成实体类的中get/set等方法
- Knife4J:接口文档工具
- **支付宝支付:alipay.com**
- MySQL:关系型数据库
- Redis:缓存
- RabbitMQ:消息中间件;大型分布式项目的标配
- MinIO(私有化对象存储集群):分布式文件存储
- **Ngrok/natapp/小米球:内网穿透工具**
- Docker:容器化技术
前端技术栈
- ECMAScript 6:也称为 ECMAScript 2015,是 JavaScript 的一个版本,引入了许多新的语法和功能。
- Vue.js:一个用于构建用户界面的渐进式JavaScript框架。
- Vuex:Vue.js 应用程序的状态管理模式和库。
- Vue Router:Vue.js 官方的路由管理器,用于构建单页面应用(SPA)。
- Vue CLI:Vue.js 开发的标准工具,提供了一个丰富的交互式项目脚手架。
- Axios:一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js,常用于发送网络请求。
- Element Plus:一套为 Vue.js 3.0 开发的桌面端 UI 组件库,提供丰富的组件以帮助快速构建界面。
## 2 环境准备
### 2.1 还原镜像
在资料中找到`xxx.tar`,上传到服务器中,并还原镜像
```bash
docker load -i minio.tar
docker load -i mysql.tar
docker load -i nacos.tar
docker load -i rabbitmq.tar
docker load -i redis.tar
docker load -i sentinel.tar
```
### 2.2 安装容器
在资料中找到`docker-compose.yml`,上传到服务器中,并执行命令
```bash
# 启动容器(如果不存在容器就创建、存在则修改)
docker compose -f docker-compose.yml up -d
# 其他命令
# 停止所有容器
docker compose -f docker-compose.yml stop
# 启动所有容器
docker compose -f docker-compose.yml start
# 重启所有容器
docker compose -f docker-compose.yml restart
# 删除所有容器
docker compose -f docker-compose.yml down
```
### 2.3 环境测试
#### 2.3.1 MySQL
MySQL如果无法远程连接,如下,则修改密码校验方式

```sql
#进入容器
docker exec -it spzx-mysql /bin/bash
#进入mysql命令行
mysql -uroot -p
#修改默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
```
#### 2.3.2 其他
- 使用redis客户端工具远程连接redis测试
- nacos控制台: `http://192.168.200.10:8848/nacos` 账号密码:nacos/nacos
- rabbitmq控制台:`http://192.168.200.10:15672` 账号密码:guest/guest
- minio控制台:`http://192.168.200.10:9001` 账号密码:admin/admin123456
- sentinel控制台:`http://192.168.200.10:8858` 账号密码:sentinel/sentinel
## 3 若依框架简介
官网:https://doc.ruoyi.vip/
微服务版本:https://doc.ruoyi.vip/ruoyi-cloud/
源码下载:https://ruoyi.vip/,下载`RuoYi-Cloud 微服务版`
环境部署:[环境部署 | RuoYi](https://doc.ruoyi.vip/ruoyi-cloud/document/hjbs.html#准备工作)
### 3.1 介绍
**RuoYi-Cloud** 是一个 Java EE 分布式微服务架构平台,基于经典技术组合(Spring Boot、Spring Cloud & Alibaba、Vue、Element),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源。
### 3.2 架构图

### 3.3 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
14. 系统接口:根据业务代码自动生成相关的api接口文档。
15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 在线构建器:拖动表单元素生成相应的HTML代码。
17. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
### 3.4 在线体验
- 若依官网:[http://ruoyi.vip(opens new window)](http://ruoyi.vip/)
- 演示地址:[http://cloud.ruoyi.vip(opens new window)](http://cloud.ruoyi.vip/)
- 代码下载:
## 4 启动项目
### 4.1 服务端
#### 4.1.1 项目版本
当前的RuoYi-Cloud版本是3.6.3,其中jdk版本为1.8,spring-boot版本为2.7.18,不是我们预期的版本。

因此我们在此基础上做了升级,升级后的版本如下:

#### 4.1.2 导入项目
第一步:找到项目初始源码`spzx-parent.rar`,解压并复制到工作空间
第二步:修改maven配置为自己的Maven相关路径
第三步:导入项目源码到idea
若依原生自带的项目模块如下:
```
com.ruoyi
├── ruoyi-ui // 前端框架 [80]
├── ruoyi-gateway // 网关模块 [8080]
├── ruoyi-auth // 认证中心 [9200]
├── ruoyi-api // 接口模块
│ └── ruoyi-api-system // 系统接口
├── ruoyi-common // 通用模块
│ └── ruoyi-common-core // 核心模块
│ └── ruoyi-common-datascope // 权限范围
│ └── ruoyi-common-datasource // 多数据源
│ └── ruoyi-common-log // 日志记录
│ └── ruoyi-common-redis // 缓存服务
│ └── ruoyi-common-seata // 分布式事务
│ └── ruoyi-common-security // 安全模块
├── ruoyi-modules // 业务模块
│ └── ruoyi-system // 系统模块 [9201]
│ └── ruoyi-gen // 代码生成 [9202]
│ └── ruoyi-job // 定时任务 [9203]
│ └── ruoyi-file // 文件服务 [9300]
├── ruoyi-visual // 图形化管理模块
│ └── ruoyi-visual-monitor // 监控中心 [9100]
├──pom.xml // 公共依赖
```
我们将其包名和模块名进行了修改如下:
```
com.spzx
├── spzx-gateway // 网关模块 [8080]
├── spzx-auth // 认证中心 [9200]
├── spzx-api // 接口模块
│ └── spzx-api-system // 系统接口
├── spzx-common // 通用模块
│ └── spzx-common-core // 核心模块
│ └── spzx-common-datascope // 权限范围
│ └── spzx-common-datasource // 多数据源
│ └── spzx-common-log // 日志记录
│ └── spzx-common-redis // 缓存服务
│ └── spzx-common-seata // 分布式事务
│ └── spzx-common-security // 安全模块
├── spzx-modules // 业务模块
│ └── spzx-system // 系统模块 [9201]
│ └── spzx-gen // 代码生成 [9202]
│ └── spzx-file // 文件服务 [9300]
├── spzx-visual // 图形化管理模块
│ └── spzx-monitor // 监控中心 [9100]
├──pom.xml // 公共依赖
```
#### 4.1.3 导入数据
执行数据库脚本:在资料中找到 `spzx-xxx.sql`
#### 4.1.4 导入nacos配置文件
第一步:导入配置
- 启动nacos,访问nacos控制台
- 在资料中找到 `nacos配置`
- 在nacos控制台:配置管理 => 配置列表 => 导入配置 => 选择压缩包
第二步:修改nacos配置。涉及数据源的地方都要修改,mysql、redis、minio等等,地址需要修改为自己虚拟机的地址
第三步:修改spzx-gateway、spzx-auth、spzx-system、spzx-file、spzx-gen、spzx-monitor块的本地配置文件。涉及nacos配置中心、注册中心地址、sentinel地址等
第四步:启动如
下模块:spzx-gateway、spzx-auth、spzx-system、spzx-file、spzx-gen、spzx-monitor
### 4.2 前端
替换前端项目:在资料中找到 `spzx-ui.zip`,解压文件
```shell
# node版本 v18
node -v
#进入前端目录
cd spzx-ui
# 安装依赖
npm install
# 本地开发 启动项目
npm run dev
# 构建测试环境 npm run build:stage
# 构建生产环境 npm run build:prod
```
默认登录用户名:admin/admin123
## 5 创建商品模块
### 5.1 新建spzx-product模块
**第一次基于若依创建模块可以参考spzx-system**
在spzx-modules模块下新建子模块spzx-product
### 5.2 pom.xml
```xml
4.0.0
com.spzx
spzx-modules
3.6.3
spzx-product
spzx-product系统模块
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
org.springframework.boot
spring-boot-starter-actuator
com.mysql
mysql-connector-j
com.spzx
spzx-common-datascope
com.spzx
spzx-common-log
${project.artifactId}
org.springframework.boot
spring-boot-maven-plugin
repackage
org.apache.maven.plugins
maven-compiler-plugin
17
17
```
### 5.3 banner.txt
在resources目录下新建banner.txt
```text
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}
_ _
(_) | |
_ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___
| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \
| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | |
|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_|
__/ | __/ |
|___/ |___/
```
### 5.4 bootstrap.yml
在resources目录下新建bootstrap.yml
```yaml
# Tomcat
server:
port: 9205
# Spring
spring:
application:
# 应用名称
name: spzx-product
profiles:
# 环境配置
active: dev
main:
allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.200.10:8848
config:
# 配置中心地址
server-addr: 192.168.200.10:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
```
### 5.5 spzx-product-dev.yml
在nacos上添加商品服务配置文件
```yaml
mybatis-plus:
mapper-locations: classpath*:mapper/**/*Mapper.xml
type-aliases-package: com.spzx.**.domain
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
global-config:
db-config:
logic-delete-field: del_flag # 全局逻辑删除的实体字段名
logic-delete-value: 2 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
# spring配置
spring:
data:
redis:
host: 192.168.200.10
port: 6379
password:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.200.10:3306/spzx-product?characterEncoding=utf-8&useSSL=false
username: root
password: root
hikari:
connection-test-query: SELECT 1
connection-timeout: 60000
idle-timeout: 500000
max-lifetime: 540000
maximum-pool-size: 10
minimum-idle: 5
pool-name: GuliHikariPool
```
### 5.6 logback.xml
在resources目录下新建logback.xml
```xml
${log.pattern}
${log.path}/info.log
${log.path}/info.%d{yyyy-MM-dd}.log
60
${log.pattern}
INFO
ACCEPT
DENY
${log.path}/error.log
${log.path}/error.%d{yyyy-MM-dd}.log
60
${log.pattern}
ERROR
ACCEPT
DENY
```
### 5.7 SpzxProductApplication
添加启动类
```java
package com.spzx.product;
/**
* 商品模块
*
* @author spzx
*/
@EnableCustomConfig
@EnableRyFeignClients
@SpringBootApplication
public class SpzxProductApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpzxProductApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 系统模块启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}
```
### 5.8 启动测试
## 6 MybatisPlus代码生成器
参考文档:[代码生成器 | MyBatis-Plus](https://baomidou.com/guides/new-code-generator/)
### 6.1 添加依赖
在spzx-parent的dependencyManagement中添加依赖
```xml
com.baomidou
mybatis-plus-generator
${mybatis-plus.version}
org.freemarker
freemarker
2.3.32
```
在spzx-common-core中添加依赖
```xml
com.baomidou
mybatis-plus-generator
org.freemarker
freemarker
```
### 6.2 创建代码生成器
在test目录中创建代码生成器
```java
package com.spzx.product;
public class GenMP {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://192.168.200.10:3306/spzx-product?characterEncoding=utf-8&useSSL=false", "root", "root")
.globalConfig(builder -> builder
.author("atguigu")
.outputDir("D:/spzx/product")
.dateType(DateType.ONLY_DATE)
.disableOpenDir()
)
.packageConfig(builder -> builder
.parent("com.spzx.product")
.entity("domain")
.mapper("mapper")
.xml("mapper.product")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
)
.strategyConfig(builder -> builder
.addInclude(
"brand",
"category",
"category_brand",
"product",
"product_details",
"product_sku",
"product_spec",
"product_unit",
"sku_stock") // 设置需要生成的表名
.entityBuilder()
.enableLombok()
.superClass(BaseEntity.class)
.addSuperEntityColumns(
"id",
"create_by",
"create_time",
"update_by",
"update_time",
"remark",
"del_flag")
//.addIgnoreColumns
.enableFileOverride()
.serviceBuilder()
//.formatServiceFileName("I%sService")
.enableFileOverride()
.controllerBuilder()
.superClass(BaseController.class)
.enableRestStyle()
.enableFileOverride()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
```
### 6.3 复制代码
将java文件复制在src/main/java
将XxxMapper.xml文件复制到src/main/resources/mapper/product目录下
## 7 代码推送到远程仓库
### 7.1 新建远程仓库
创建远程仓库并获取仓库地址

### 7.2 初始化本地仓库

### 7.3 提交并推送代码


## 8 文件上传服务
### 8.1 创建bucket
1、访问minio管理页面:http://192.168.200.10:9001 账号密码:admin/admin123456
2、创建bucket:spzx
3、设置访问权限
```json
{
"Statement" : [ {
"Action" : "s3:GetObject",
"Effect" : "Allow",
"Principal" : "*",
"Resource" : "arn:aws:s3:::spzx/*"
} ],
"Version" : "2012-10-17"
}
```
### 8.2 测试文件上传
1、启动spzx-file微服务
2、访问swagger测试文件上传:http://localhost:9300/doc.html
同步时间:
```sh
第一步:安装ntp服务
yum -y install ntp
第二步:开启开机启动服务
systemctl enable ntpd
第三步:启动服务 Tips:联网正常前提下如果定时同步失败,先停止服务,再启动
systemctl stop ntpd
systemctl start ntpd
第四步:更改时区
timedatectl set-timezone Asia/Shanghai
第五步:启用ntp同步
timedatectl set-ntp yes
第六步:同步时间
ntpq -p
```
## 9 品牌管理
品牌管理就是对商品的所涉及到的品牌数据进行维护。

### 9.1 品牌列表
#### 9.1.1 Brand
```java
package com.spzx.product.domain;
@Schema(description = "品牌")
@Getter
@Setter
@TableName("brand")
public class Brand extends BaseEntity
{
private static final long serialVersionUID = 1L;
@Schema(description = "品牌名称")
private String name;
@Schema(description = "品牌图标")
private String logo;
}
```
#### 9.1.2 BrandController
查询品牌分页列表接口
```java
package com.spzx.product.controller;
@Tag(name = "品牌管理")
@RestController
@RequestMapping("/brand")
public class BrandController extends BaseController{
@Autowired
private IBrandService brandService;
@Operation(summary = "查询品牌列表")
@GetMapping("/list")
public TableDataInfo list(@Parameter(description = "品牌名称") String name){
//分页参数封装到了BaseController类
startPage();
List list = brandService.selectBrandList(name);
return getDataTable(list);
}
}
```
#### 9.1.3 IBrandService
```java
List selectBrandList(String name);
```
#### 9.1.4 BrandServiceImpl
```java
@Override
public List selectBrandList(String name) {
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.hasText(name), Brand::getName, name);
return baseMapper.selectList(queryWrapper);
}
```
#### 9.1.5 测试接口
启动项目
访问在线swg:http://localhost:9205/doc.html
注意分页参数使用:?pageNum=1&pageSize=10
### 9.2 获取品牌详细信息
#### BrandController
```java
@Operation(summary = "获取品牌详细信息")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id){
return success(brandService.getById(id));
}
```
### 9.3 新增品牌
#### 9.3.1 BrandController
```java
@Operation(summary = "新增品牌")
@PostMapping
public AjaxResult add(@Parameter(description = "品牌") @RequestBody Brand brand) {
//设置当前用户
brand.setCreateBy(SecurityUtils.getUsername());
return toAjax(brandService.save(brand));
}
```
#### 9.3.2 测试接口
测试数据
```json
{
"name": "大米",
"logo": "http://139.198.127.41:9000/spzx/2024/02/03/xm_20240203154528A007.png"
}
```
### 9.4 修改品牌
#### 9.4.1 BrandController
```java
@Operation(summary = "修改品牌")
@PutMapping
public AjaxResult edit(@Parameter(description = "品牌") @RequestBody Brand brand) {
//设置当前用户
brand.setUpdateBy(SecurityUtils.getUsername());
return toAjax(brandService.updateById(brand));
}
```
#### 9.4.2 测试接口
测试数据
```json
{
"id": 7,
"name": "中米"
}
```
### 9.5 删除品牌
#### 9.5.1 BrandController
```java
@Operation(summary = "删除品牌")
@DeleteMapping("/{ids}")
public AjaxResult remove(
@Parameter(description = "品牌id列表", example = "[1,2]")
@PathVariable List ids) {
return toAjax(brandService.removeBatchByIds(ids));
}
```
#### 9.5.2 测试接口
测试数据:多个id用逗号分隔
### 9.6 前端整合
#### 9.6.1 配置网关
在nacos配置中心:spzx-gateway-dev.yml文件添加商品服务的路由配置信息
```yaml
# 商品服务
- id: spzx-product
uri: lb://spzx-product
predicates:
- Path=/product/**
filters:
- StripPrefix=1
```
#### 9.6.2 添加动态菜单
系统管理 -> 菜单管理 -> 新增 -> 主类目
商品管理

品牌管理

## 10 权限控制
### 10.1 介绍
`权限控制`主要目的是保护系统的安全性和完整性,防止未经授权的用户获取敏感信息、执行非法操作或对系统进行恶意操作 。
常见的权限控制框架有 **SpringSecurity** 和 Apache Shiro。
若依的权限管理是通过 `RBAC`(Role-based Access Control 基于角色的访问控制)模型自己设计的。
RBAC模型将权限控制分为角色管理和权限管理两个部分。通过为每个角色分配相应的权限,可以实现对系统的全面管理和控制。
### 10.2 表结构设计
在数据库表结构方面,若依采用了RBAC模型的设计。其中,主要包括以下表:
- `sys_menu`:系统菜单(权限)
- `sys_role`:系统角色
- `sys_user`:系统用户
- `sys_role_menu`:角色和菜单之间的关联关系
- `sys_user_role`:用户和角色之间的关联关系
### 10.3 添加按钮
sys_menu表的数据分为三个级别:目录、菜单、按钮。现在我们在`品牌管理`菜单下添加按钮:

### 10.4 分配权限
在`系统管理=>用户管理`创建用户:

在`系统管理=>角色管理`创建角色,并为角色分配菜单权限和用户:



### 10.5 前端权限控制
spzx-ui/src/views/product/brand/index.vue
```vue
新增
```
### 10.6 接口权限控制
前端会根据权限标志显示或者隐藏按钮,但是如果用户不点击按钮,直接通过http请求工具请求后端咋办?所以接口权限也是要有的。
#### 10.6.1 实现原理
若依系统实现了这部分功能,实现模块:`spzx-common-security`。
底层原理:拦截器`HeaderInterceptor`负责解析用户token信息,并将解析出的用户信息存入`ThreadLocal`中,然后`PreAuthorizeAspect`负责解析权限注解,判断当前用户是否拥有注解权限。

#### 10.6.2 基本用法
`@RequiresLogin`注解用于配置接口要求用户必须登录才可访问
示例1: 以下代码表示必须登录才可访问
```java
@RequiresLogin
public AjaxResult allCheckCart(){
return success();
}
```
`@RequiresPermissions`注解用于配置接口要求用户拥有某(些)权限才可访问,它拥有两个参数
| 参数 | 类型 | 描述 |
| ------- | -------- | ------------------------------------- |
| value | String[] | 权限列表 |
| logical | Logical | 权限之间的判断关系,默认为Logical.AND |
示例1: 以下代码表示必须拥有`product:brand:add`权限才可访问
```java
@RequiresPermissions("product:brand:add")
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
示例2: 以下代码表示必须拥有`product:brand:add`和`product:brand:edit`权限才可访问
```java
@RequiresPermissions({"product:brand:add", "product:brand:edit"})
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
示例3: 以下代码表示需要拥有`product:brand:add`或`product:brand:edit`权限才可访问
```java
@RequiresPermissions(value = {"product:brand:add", "product:brand:edit"}, logical = Logical.OR)
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
`@RequiresRoles`注解用于配置接口要求用户拥有某(些)角色才可访问,它拥有两个参数
| 参数 | 类型 | 描述 |
| ------- | -------- | ------------------------------------- |
| value | String[] | 角色列表 |
| logical | Logical | 角色之间的判断关系,默认为Logical.AND |
示例1: 以下代码表示必须拥有`admin`角色才可访问
```java
@RequiresRoles("admin")
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
示例2: 以下代码表示必须拥有`admin`和`productAdmin`角色才可访问
```java
@RequiresRoles({"admin", "productAdmin"})
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
示例3: 以下代码表示需要拥有`admin`或`productAdmin`角色才可访问
```java
@RequiresRoles(value = {"admin", "common"}, logical = Logical.OR)
public AjaxResult save(...)
{
return AjaxResult.success(...);
}
```
#### 10.6.3 品牌管理添加注解
```java
@RequiresPermissions("product:brand:list")
@Operation(summary = "查询品牌列表")
@RequiresPermissions("product:brand:query")
@Operation(summary = "获取品牌详细信息")
@RequiresPermissions("product:brand:add")
@Operation(summary = "新增品牌")
@RequiresPermissions("product:brand:edit")
@Operation(summary = "修改品牌")
@RequiresPermissions("product:brand:remove")
@Operation(summary = "删除品牌")
```
**注意:**如果修改了角色权限,才需要重新登录用户账号,以便获取最新的角色权限信息
#### 10.6.4 接口测试
在swagger中直接测试,没有操作权限或未登录:


在前端发送的请求中获取请求头:

在Swagger中配置请求头,模拟账号登录:

再次测试,则可以成功访问接口。
## 11 系统日志
### 11.1 完善系统日志
以品牌管理为例,在需要被记录日志的`controller`方法上添加`@Log`注解,使用方法如下:
```java
@Log(title = "品牌管理", businessType = BusinessType.INSERT)
@RequiresPermissions("product:brand:add")
@Operation(summary = "新增品牌")
@Log(title = "品牌管理", businessType = BusinessType.UPDATE)
@RequiresPermissions("product:brand:edit")
@Operation(summary = "修改品牌")
@Log(title = "品牌管理", businessType = BusinessType.DELETE)
@RequiresPermissions("product:brand:remove")
@Operation(summary = "删除品牌")
```
在后台系统的`系统管理 => 日志管理`中查看操作日志:

### 11.2 实现原理
若依系统实现了这部分功能,实现模块:`spzx-common-log`。
`LogAspect`负责解析日志注解`Log`,调用`AsyncLogService`利用异步方式保存日志。

### 11.3 自定义操作功能
1、在`BusinessType`中新增业务操作类型如:
```java
/**
* 测试
*/
TEST,
```
2、在`sys_dict_data`字典数据表中初始化操作业务类型
选择操作类型:

添加字典数据:

3、在`Controller`中使用注解
```java
@Log(title = "测试", businessType = BusinessType.TEST)
@Operation(summary = "测试")
@DeleteMapping("/test")
public AjaxResult test() {
return success();
}
```