[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如果无法远程连接,如下,则修改密码校验方式 ![image-20240418165646147](images/image-20240418165646147.png) ```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 微服务版` 70832969745 环境部署:[环境部署 | 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 架构图 ![fe8a5f53_1915710](images/fe8a5f53_1915710.png) ### 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,不是我们预期的版本。 ![70832987768](images/1708329877689.png) 因此我们在此基础上做了升级,升级后的版本如下: ![70833011254](images/1708330112547.png) #### 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 新建远程仓库 创建远程仓库并获取仓库地址 ![image-20241108005517603](images/image-20241108005517603.png) ### 7.2 初始化本地仓库 ![image-20241108010107352](images/image-20241108010107352.png) ### 7.3 提交并推送代码 ![image-20241108010457416](images/image-20241108010457416.png) ![image-20241108010842895](images/image-20241108010842895.png) ## 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 品牌管理 品牌管理就是对商品的所涉及到的品牌数据进行维护。 ![image-20241108031754274](images/image-20241108031754274.png) ### 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 添加动态菜单 系统管理 -> 菜单管理 -> 新增 -> 主类目 商品管理 ![image-20241108031040120](images/image-20241108031040120.png) 品牌管理 ![image-20241108031425834](images/image-20241108031425834.png) ## 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表的数据分为三个级别:目录、菜单、按钮。现在我们在`品牌管理`菜单下添加按钮: ![image-20241110212253840](images/image-20241110212253840.png) ### 10.4 分配权限 在`系统管理=>用户管理`创建用户: ![image-20241110204359995](images/image-20241110204359995.png) 在`系统管理=>角色管理`创建角色,并为角色分配菜单权限和用户: ![image-20241110212455813](images/image-20241110212455813.png) ![image-20241110204611269](images/image-20241110204611269.png) ![image-20241110204712986](images/image-20241110204712986.png) ### 10.5 前端权限控制 spzx-ui/src/views/product/brand/index.vue ```vue 新增 ``` ### 10.6 接口权限控制 前端会根据权限标志显示或者隐藏按钮,但是如果用户不点击按钮,直接通过http请求工具请求后端咋办?所以接口权限也是要有的。 #### 10.6.1 实现原理 若依系统实现了这部分功能,实现模块:`spzx-common-security`。 底层原理:拦截器`HeaderInterceptor`负责解析用户token信息,并将解析出的用户信息存入`ThreadLocal`中,然后`PreAuthorizeAspect`负责解析权限注解,判断当前用户是否拥有注解权限。 ![image-20241110210145624](images/image-20241110210145624.png) #### 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中直接测试,没有操作权限或未登录: ![image-20241110221934369](images/image-20241110221934369.png) ![image-20241113105303394](images/image-20241113105303394.png) 在前端发送的请求中获取请求头: ![image-20241110222547301](images/image-20241110222547301.png) 在Swagger中配置请求头,模拟账号登录: ![image-20241110222730780](images/image-20241110222730780.png) 再次测试,则可以成功访问接口。 ## 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 = "删除品牌") ``` 在后台系统的`系统管理 => 日志管理`中查看操作日志: ![image-20241110222956095](images/image-20241110222956095.png) ### 11.2 实现原理 若依系统实现了这部分功能,实现模块:`spzx-common-log`。 `LogAspect`负责解析日志注解`Log`,调用`AsyncLogService`利用异步方式保存日志。 ![image-20241110221107923](images/image-20241110221107923.png) ### 11.3 自定义操作功能 1、在`BusinessType`中新增业务操作类型如: ```java /** * 测试 */ TEST, ``` 2、在`sys_dict_data`字典数据表中初始化操作业务类型 选择操作类型: ![image-20240926170244551](images/image-20240926170244551.png) 添加字典数据: ![image-20241110221630445](images/image-20241110221630445.png) 3、在`Controller`中使用注解 ```java @Log(title = "测试", businessType = BusinessType.TEST) @Operation(summary = "测试") @DeleteMapping("/test") public AjaxResult test() { return success(); } ```