尚硅谷_尚品甄选_第1章 项目搭建.md 37 KB

[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,上传到服务器中,并还原镜像

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,上传到服务器中,并执行命令

# 启动容器(如果不存在容器就创建、存在则修改)
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

#进入容器
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

3.1 介绍

RuoYi-Cloud 是一个 Java EE 分布式微服务架构平台,基于经典技术组合(Spring Boot、Spring Cloud & Alibaba、Vue、Element),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源。

3.2 架构图

fe8a5f53_1915710

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 在线体验

4 启动项目

4.1 服务端

4.1.1 项目版本

当前的RuoYi-Cloud版本是3.6.3,其中jdk版本为1.8,spring-boot版本为2.7.18,不是我们预期的版本。

70832987768

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

70833011254

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,解压文件

# 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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.spzx</groupId>
        <artifactId>spzx-modules</artifactId>
        <version>3.6.3</version>
    </parent>

    <artifactId>spzx-product</artifactId>

    <description>
        spzx-product系统模块
    </description>

    <dependencies>
        <!-- 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 DataScope -->
        <dependency>
            <groupId>com.spzx</groupId>
            <artifactId>spzx-common-datascope</artifactId>
        </dependency>

        <!-- RuoYi Common Log -->
        <dependency>
            <groupId>com.spzx</groupId>
            <artifactId>spzx-common-log</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>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

5.3 banner.txt

在resources目录下新建banner.txt

Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}
                            _                           _                    
                           (_)                         | |                   
 _ __  _   _   ___   _   _  _  ______  ___  _   _  ___ | |_   ___  _ __ ___  
| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ 
| |   | |_| || (_) || |_| || |        \__ \| |_| |\__ \| |_ |  __/| | | | | |
|_|    \__,_| \___/  \__, ||_|        |___/ \__, ||___/ \__| \___||_| |_| |_|
                      __/ |                  __/ |                           
                     |___/                  |___/                            

5.4 bootstrap.yml

在resources目录下新建bootstrap.yml

# 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上添加商品服务配置文件

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 version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 日志存放路径 -->
   <property name="log.path" value="logs/spzx-product" />
   <!-- 日志输出格式 -->
   <property name="log.pattern" value="%yellow(%d{HH:mm:ss.SSS}) [%thread] %green(%-5level) %cyan(%logger{20}) - [%method,%line] - %msg%n" />

    <!-- 控制台输出 -->
   <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>${log.pattern}</pattern>
      </encoder>
   </appender>

    <!-- 系统日志输出 -->
   <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
       <file>${log.path}/info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
         <fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
         <!-- 日志最大的历史 60天 -->
         <maxHistory>60</maxHistory>
      </rollingPolicy>
      <encoder>
         <pattern>${log.pattern}</pattern>
      </encoder>
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
   </appender>

    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
       <file>${log.path}/error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
         <!-- 日志最大的历史 60天 -->
         <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
         <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
         <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 系统模块日志级别控制  -->
   <logger name="com.spzx" level="info" />
   <!-- Spring日志级别控制  -->
   <logger name="org.springframework" level="warn" />

   <root level="info">
      <appender-ref ref="console" />
   </root>

   <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="file_info" />
        <appender-ref ref="file_error" />
    </root>
</configuration>

5.7 SpzxProductApplication

添加启动类

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

6.1 添加依赖

在spzx-parent的dependencyManagement中添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>

在spzx-common-core中添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
</dependency>

6.2 创建代码生成器

在test目录中创建代码生成器

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

7.2 初始化本地仓库

image-20241108010107352

7.3 提交并推送代码

image-20241108010457416

image-20241108010842895

8 文件上传服务

8.1 创建bucket

1、访问minio管理页面:http://192.168.200.10:9001 账号密码:admin/admin123456

2、创建bucket:spzx

3、设置访问权限

{
  "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

同步时间:

第一步:安装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

9.1 品牌列表

9.1.1 Brand

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

查询品牌分页列表接口

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<Brand> list = brandService.selectBrandList(name);
        return getDataTable(list);
    }
}

9.1.3 IBrandService

List<Brand> selectBrandList(String name);

9.1.4 BrandServiceImpl

@Override
public List<Brand> selectBrandList(String name) {
    LambdaQueryWrapper<Brand> 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

@Operation(summary = "获取品牌详细信息")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id){
    return success(brandService.getById(id));
}

9.3 新增品牌

9.3.1 BrandController

@Operation(summary = "新增品牌")
@PostMapping
public AjaxResult add(@Parameter(description = "品牌") @RequestBody Brand brand) {
    //设置当前用户
    brand.setCreateBy(SecurityUtils.getUsername());
    return toAjax(brandService.save(brand));
}

9.3.2 测试接口

测试数据

{
  "name": "大米",
  "logo": "http://139.198.127.41:9000/spzx/2024/02/03/xm_20240203154528A007.png"
}

9.4 修改品牌

9.4.1 BrandController

@Operation(summary = "修改品牌")
@PutMapping
public AjaxResult edit(@Parameter(description = "品牌") @RequestBody Brand brand) {
    //设置当前用户
    brand.setUpdateBy(SecurityUtils.getUsername());
    return toAjax(brandService.updateById(brand));
}

9.4.2 测试接口

测试数据

{
  "id": 7,
  "name": "中米"
}

9.5 删除品牌

9.5.1 BrandController

@Operation(summary = "删除品牌")
@DeleteMapping("/{ids}")
public AjaxResult remove(
    @Parameter(description = "品牌id列表", example = "[1,2]") 
    @PathVariable List<Long> ids) {
    return toAjax(brandService.removeBatchByIds(ids));
}

9.5.2 测试接口

测试数据:多个id用逗号分隔

9.6 前端整合

9.6.1 配置网关

在nacos配置中心:spzx-gateway-dev.yml文件添加商品服务的路由配置信息

		# 商品服务
        - id: spzx-product
          uri: lb://spzx-product
          predicates:
            - Path=/product/**
          filters:
            - StripPrefix=1

9.6.2 添加动态菜单

系统管理 -> 菜单管理 -> 新增 -> 主类目

商品管理

image-20241108031040120

品牌管理

image-20241108031425834

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

10.4 分配权限

系统管理=>用户管理创建用户:

image-20241110204359995

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

image-20241110212455813

image-20241110204611269

image-20241110204712986

10.5 前端权限控制

spzx-ui/src/views/product/brand/index.vue

<el-button type="primary" plain icon="Plus" 
           @click="handleAdd" 
           v-hasPermi="['product:brand:add']">
    新增
</el-button>

10.6 接口权限控制

前端会根据权限标志显示或者隐藏按钮,但是如果用户不点击按钮,直接通过http请求工具请求后端咋办?所以接口权限也是要有的。

10.6.1 实现原理

若依系统实现了这部分功能,实现模块:spzx-common-security

底层原理:拦截器HeaderInterceptor负责解析用户token信息,并将解析出的用户信息存入ThreadLocal中,然后PreAuthorizeAspect负责解析权限注解,判断当前用户是否拥有注解权限。

image-20241110210145624

10.6.2 基本用法

@RequiresLogin注解用于配置接口要求用户必须登录才可访问

示例1: 以下代码表示必须登录才可访问

@RequiresLogin
public AjaxResult allCheckCart(){
    return success();
}

@RequiresPermissions注解用于配置接口要求用户拥有某(些)权限才可访问,它拥有两个参数

参数 类型 描述
value String[] 权限列表
logical Logical 权限之间的判断关系,默认为Logical.AND

示例1: 以下代码表示必须拥有product:brand:add权限才可访问

@RequiresPermissions("product:brand:add")
public AjaxResult save(...) 
{
    return AjaxResult.success(...);
}

示例2: 以下代码表示必须拥有product:brand:addproduct:brand:edit权限才可访问

@RequiresPermissions({"product:brand:add", "product:brand:edit"})
public AjaxResult save(...)
{
    return AjaxResult.success(...);
}

示例3: 以下代码表示需要拥有product:brand:addproduct:brand:edit权限才可访问

@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角色才可访问

@RequiresRoles("admin")
public AjaxResult save(...)
{
    return AjaxResult.success(...);
}

示例2: 以下代码表示必须拥有adminproductAdmin角色才可访问

@RequiresRoles({"admin", "productAdmin"})
public AjaxResult save(...)
{
    return AjaxResult.success(...);
}

示例3: 以下代码表示需要拥有adminproductAdmin角色才可访问

@RequiresRoles(value = {"admin", "common"}, logical = Logical.OR)
public AjaxResult save(...)
{
    return AjaxResult.success(...);
}

10.6.3 品牌管理添加注解

@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

image-20241113105303394

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

image-20241110222547301

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

image-20241110222730780

再次测试,则可以成功访问接口。

11 系统日志

11.1 完善系统日志

以品牌管理为例,在需要被记录日志的controller方法上添加@Log注解,使用方法如下:

@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

11.2 实现原理

若依系统实现了这部分功能,实现模块:spzx-common-log

LogAspect负责解析日志注解Log,调用AsyncLogService利用异步方式保存日志。

image-20241110221107923

11.3 自定义操作功能

1、在BusinessType中新增业务操作类型如:

/**
 * 测试
 */
TEST,

2、在sys_dict_data字典数据表中初始化操作业务类型

选择操作类型:

image-20240926170244551

添加字典数据:

image-20241110221630445

3、在Controller中使用注解

@Log(title = "测试", businessType = BusinessType.TEST)
@Operation(summary = "测试")
@DeleteMapping("/test")
public AjaxResult test() {
    return success();
}