【JavaWeb】——(若依 + AI)-基础学习笔记

1. 初识若依

1.1. 若依搭建

  • 后端项目克隆与初始化

    • 拉取项目:git clone https://gitee.com/y_project/RuoYi-Vue.git

    • 从gitee下载项目完毕后,每个模块高亮加

      • 如果没有下载完毕,就在maven生命周期中依次点击clean => package
  • 基础配置

    1. 创建数据库

      这里我将数据库创建到云端,并用navicat连接了,也可以本地创建数据库

      执行sql文件夹的两个sql文件,建立表格并初始化

      配置项目数据库连接(改为自己的数据库)

    2. 配置redis

      这里redis在本地的话不需要怎么配置,如果像我一样redis也连的服务器(因为我懒得每次开发都启动),就要改一下

  • 启动项目成功

    可能遇到的警告

    WARNING: A terminally deprecated method in sun.misc.Unsafe has been called

    WARNING: sun.misc.Unsafe::objectFieldOffset has been called by io.netty.util.internal.PlatformDependent0$4 (file:/C:/Users/xxx/.m2/repository/io/netty/netty-common/4.1.92.Final/netty-common-4.1.92.Final.jar)

    WARNING: Please consider reporting this to the maintainers of class io.netty.util.internal.PlatformDependent0$4

    WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release

    关键说明

    • 这只是 警告而非错误,不会影响项目运行(项目已正常启动,功能不受影响);
    • 根源是 JDK 9+ 引入的「模块系统(Module)」,对 System.load、System.loadLibrary 等调用 native 代码的方法做了权限限制,高版本 JDK(如 JDK 17+、24)会强化这种限制并给出警告;
    • Tomcat 9 是较稳定的版本,但未针对 JDK 24 做模块权限适配,导致触发警告
  • 前端项目搭建

    • 拉取项目:git clone https://gitee.com/ys-gitee/RuoYi-Vue3

    • 运行npm install下载依赖

    • 运行npm run dev运行项目即可

      输入验证码完成登录

1.2. 入门案例

使用若依代码生成器,生成课程管理的前后端代码

  1. 准备sql语句并导入数据库

    sql建表语句

    sql 复制代码
    -- MySQL dump 10.13  Distrib 8.0.31, for Win64 (x86_64)
    --
    -- Host: 127.0.0.1    Database: ry-vue
    -- ------------------------------------------------------
    -- Server version	8.0.31
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!50503 SET NAMES utf8mb4 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    
    --
    -- Table structure for table `tb_course`
    --
    
    DROP TABLE IF EXISTS `tb_course`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!50503 SET character_set_client = utf8mb4 */;
    CREATE TABLE `tb_course` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT '课程id',
      `code` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程编码',
      `subject` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程学科',
      `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程名称',
      `price` int DEFAULT NULL COMMENT '价格(元)',
      `applicable_person` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '适用人群',
      `info` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程介绍',
      `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='课程管理';
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `tb_course`
    --
    
    LOCK TABLES `tb_course` WRITE;
    /*!40000 ALTER TABLE `tb_course` DISABLE KEYS */;
    INSERT INTO `tb_course` VALUES (1,'cp123456','javaEE','JavaSE基础',199,'小白学员','JavaSE基础','2024-04-20 09:57:35','2024-04-20 09:57:35'),(2,'cp123457','javaEE','JavaWeb',188,'初级开发者','JavaWeb','2024-04-20 09:57:35','2024-04-20 09:57:35'),(3,'cp123458','Python+大数据','Python入门',555,'小白学员','Python入门','2024-04-20 09:57:35','2024-04-20 09:57:35'),(4,'cp123459','Python+大数据','PythonWeb',88,'初级开发者','PythonWeb','2024-04-20 09:57:35','2024-04-20 09:57:35'),(5,'cp123460','鸿蒙应用开发','鸿蒙入门',99,'小白学员','鸿蒙入门','2024-04-20 09:57:35','2024-04-20 09:57:35'),(6,'cp123461','鸿蒙应用开发','鸿蒙商城实战',59,'初级开发者','鸿蒙商城实战','2024-04-20 09:57:35','2024-04-20 09:57:35');
    /*!40000 ALTER TABLE `tb_course` ENABLE KEYS */;
    UNLOCK TABLES;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    -- Dump completed on 2024-04-21 17:45:54
  2. 配置代码生成信息

    • 配置基本信息

    • 配置字段信息

    • 配置生成信息

    • 下载代码

  3. 导入代码

    • 数据库导入

      解压后如图

      执行sql文件,发现sys_menu表中,课程菜单名已经导入

    • 前端导入

      复制api和views下的course文件夹,到前端项目对应的api和views文件夹下

      注意 :course文件夹下还有一个course文件夹,++我们要复制的是外层的course文件夹++

    • 后端导入

      复制java的com文件夹和resources下的mapper文件夹,项目中的java文件夹和resources文件夹下

      课程模块已经开发完毕!!!

  • 我们上面配置配置生成信息时上级菜单是0所以,在根目录下,我们还可以如图选择课程管理在菜单中的位置

2. 功能详解

2.1. 系统管理

2.1.1. 权限控制
  • 若依内置了强大的权限控制系统,为企业级项目提供了通用的解决方案。

  • 以CRM系统为例,演示下权限功能(地址:https://huike-crm.itheima.net/)

    • demo账号(超级管理员),查看所有功能菜单
    • zhangsan账号(市场专员),查看线索菜单
    • yueyue账号(销售专员),查看商机、合同等菜单
  • RBAC

    RBAC(基于角色的访问控制)是一种广泛使用的访问控制模型,通过角色来分配和管理用户的菜单权限

  • 数据表

    用户表和角色表,角色表和权限表,都是多对多关系,所以一共需要5张表

    着5张表对应的是3张主体表和2张中间表,下面是表关系说明

2.1.2. 菜单管理
  • 添加菜单

    • 上级菜单:主目录表示说顶级菜单,没有父级菜单

    • 菜单类型

      菜单类型 功能定位 显示位置
      目录 分组管理下级菜单 菜单列表的上层节点
      菜单 对应具体功能页面 菜单列表的可点击项
      按钮 对应页面内的操作功能 所属菜单的页面中
    • 显示排序:菜单的排列优先级,越小越靠前

    • 路由地址 :++必须和前端路由地址对应++ ,如果对应不上,会显示空白,目录通过菜单层级来映射路由层级

      以下面的配置为例,数据分析菜单的路由地址为data_nalysis,如果其他菜单的上级菜单选择了数据分析菜单作为上级菜单,然后路由地址配置为output,那么对应的前端真实路由地址为:/data_nalysis/output

      创建完毕后刷新页面即可(如图,页面空白,因为我们没有编写对应的前端页面做路由绑定)

  • 权限菜单实验

    1. 创建数据分析员并开放权限如图(这里的权限字符和路由无关哈,自己定义)


    2. 创建角色为数据分析员的用户


    3. 登录我刚刚创建的用户账号,只能看到首页和用户数据了

2.1.3. 字典管理
  • 数据字典

    若依内置的数据字典,用于维护系统中常见的静态数据(不经常改变)。例如:性别、状态...

  • 修改性别称谓

    我们可以通过修改数据字典,来实现对项目中使用该字典的所有值的修改

    性别称谓发生改变

  • 数据字典的表结构

    • 数字字典发表分为字典类型表和字典数据表,两个表是一对多的关系,通过dict_id和dict_code连接

    • 数据字典在数据库储存的是0,1,2等简单的数据(dict_value),这可以优化数据库性能

  • 将课程管理的学科字段改为数据字典来维护

    1. 添加字典类型和数据

      创建数据字典

      创建数据字典学科字段

    2. 修改代码生成信息

    3. 下载代码,导入前端

      用代码生成器生成新的代码,并覆盖前端代码(因为后端代码是没有变的,变的只是数据库数据和前端代码,数据库变的是数值和对应字段的映射,前端则是提交方式改为了下拉框)。

      修改完后,课程管理的编辑弹窗的课程学科,由文本框改为了下拉菜单,下拉选项也变为了刚刚我们填写的数据字典的字段。

      当我们选择JavaSE基础并提交后,可以看到,我们前端虽然还是传的数字"0",直接储存到数据库,如果要取查数据的话,查到对应的数字,再通过sys_dict_data表查出具体的名称返回

2.1.4. 其他功能
  • 参数设置

    通过参数设置,可以对系统中的参数进行动态维护(不需要改源码,也不需要重启服务器,就可以设置成果生效),比如这里可以将验证码关闭

  • 通知公告

    若依仅支持公告、通知的基础功能,如果需要对接第三方服务需要自己进行二次开发,后续有二次开发的公告案例

  • 日志管理

    若依的日志管理包括了操作日志和登录日志,但是随之程序运行时间增大,日志会越来越大,但是我们删除日志又有一定的风险,我们可以设置定任务定时导出日志。

2.2. 系统监控

2.2.1. 定时任务

若依为定时任务功能提供方便友好的web界面,实现动态管理任务。

  • 编码实现定时任务

    如果要修改,要定位代码位置并修改

  • 若依定时任务

    1. 创建任务类

      在quartz模块下的task包创建任务类

    2. 添加任务规则

      只需要写需要执行的任务的逻辑,这里以打印时间为例

    3. 启动任务

      重启项目,为项目新增定时任务如图

      • 任务方法

        1. Bean调用示例:ryTask.ryParams('ry')

        2. Class类调用示例:com.ruoyi.quarz.task.RyTask.ryParams('ry)

          参数说明:支持字符串,布尔类型,长整型,浮点型,整型

      • cron执行表达式

        就是执行周期设置,这里我设置5秒执行一次

        • 是否并发

          取决于时间任务,一般任务会支持并发提高效率。

        • 执行策略

          举个例子,我们如果服务器宕机了,处理不了任务,任务就会堆在那里,加入堆了5个任务(注意!!!,这里的任务就是指的我们上面编写的代码逻辑),现在服务器能做出运行了,下面是不同执行策略的任务执行情况。

          • 立即执行:挨个执行者5个任务

          • 执行一次:只执行这5个任务的最后一个任务

          • 放弃执行:直接跳过,不执行这5个任务

          创建好任务后,重启并开启仍未即可执行任务

2.2.2. 监控模块

若依的监控系统包括

  • 在线用户

    展示当前系统中活跃用户的信息(登录账号、所属部门、登录 IP / 地点、操作系统、浏览器、登录时间等)。

    ++用户登录后信息会存入 Redis 缓存++,该功能通过查询缓存实现,支持管理员查看和管理在线用户。

  • 数据监控

    集成Druid 监控工具(非若依自研,需二次登录),核心功能包括:

    • 数据源监控:查看数据库连接池的配置和使用状态;

    • SQL 监控:记录 SQL 执行耗时,快速定位慢 SQL 并优化;

    • SQL 防火墙:拦截危险 SQL(比如无 where 条件的更新操作),避免系统崩溃。

      密码在Druid分配置文件,初始账号为:ruoyi,密码为:123456

  • 服务监控

    实时展示服务器资源状态:CPU 使用率、内存(JVM 堆内存 + 系统物理内存)、磁盘空间、系统负载(1/5/15 分钟趋势)等。

    作用:帮助运维人员及时发现资源过载问题(比如 CPU 持续 > 85%),是排查系统卡顿、任务失败的关键工具。

  • 缓存监控

    针对 Redis 等缓存的状态监控:缓存命中率、当前连接数、内存使用占比等,支持对缓存数据的查询、删除、清空等操作,便于管理缓存资源。

  • 缓存列表

    直接展示系统中缓存的具体数据内容(比如用户会话、配置参数等),方便管理员直观查看缓存信息。

这些功能覆盖了从用户行为到服务器资源、从业务数据到缓存状态的全链路监控,是保障若依系统稳定运行的重要工具。

2.3. 系统工具

2.3.1. 表单构建

只需要开发者通过图形界面和拖拽等操作,可以快速构建复杂的表单(相比于代码生成的表单生成,表单构建能构建更加复制的表单)。

  • 表单制作按钮

    要求通过表单构建实现如图表单

    1. 制作表单并导出

      • 表单制作模块介绍

      • 表单制作容器布局

        给组件外部添加行容器,相当于给单行文本添加了父盒子,行容器和组件都有表单栅格数属性,原理就是前端的栅格布局),栅格属性的数值可理解为将父分成24份,其中子盒子占比多少份。在没有添加行容器前,表单组件的父盒子是预览视图,但是加了行容器后,表单组件的父盒子就是行容器。好像没有什么区别,但是需要注意的是,组件单独占一行,不添加行容器的话,组件就算再小,下一个组件还是会换行。

        组件属性包括是否可以清空、禁用、必填、正则校验等常规设置项

        然后再完成整个表单(默认就有确认和取消按钮)

        导出代码

    2. 复制到前端工程

      将代码放在course目录下

    3. 创建动态菜单

      这里注意组件路径是组件和views的相对路径

      刷新网页,添加课程表单创建完毕

2.3.2. 代码生成

代码生成器,根据数据库表结构自动生成前后端CRUD代码,若依提供三种生成模板:单表、树表、主子表(一对多),其中单表我们已经在入门案例中体验了。

  • 树表

    树表是一种展示层级数据的表格,能展开折叠,清晰呈现父子关系,便于管理,如公司和部门的关系。

    • 树表生成

      导入部门表

      如图,树表的层级是通过dept_id(树编码)和parent_id(父树编码)体现的。

      创建代码生成项好后,我们还可以预览根据我们刚刚的配置生成的代码

  • 主子表

    如下图中的表单,一个表单涉及到菜品表和口味表两种表的提交

2.3.3. 系统接口

当然,在若依系统中也是通过iframe来集成的swagger,你也可以直接通过swagger接口地址来访问接口文档

地址(如果在本地跑,而且是8080端口的话):http://localhost:8080/swagger-ui/index.html

  • JWT令牌

    当然除了登录接口,都需要token,如果请求头不携带token,就会报错**401**。若依的token存在了cookie里面,我们可以复制令牌(或者使用登录接口来获取token)

    填写token

    JWT认证过来之后,右边的小锁图标会变为锁定状态

  • 关于swagger的配置说明

    1. 服务器端口:8080(在 application.yml 中配置)
    2. Swagger请求前缀: /dev-api (在 application.yml 的 swagger.pathMapping 中配置)
    3. Swagger UI路径: /swagger-ui/index.html (在 ResourcesConfig.java 中配置)
    4. Swagger状态:已启用(在 application.yml 的 swagger.enabled 中配置)
  • 去除请求前缀

3. 源码阅读

3.1. 项目结构

  • 前端项目结构

  • 后端项目结构

    • 后台服务 (ruoyi-admin)

    • 通用工具ruoyi-common

    • 核心工具ruoyi-framework

    • 代码生成ruoyi-generator)和定时任务(ruoyi-quartz

  • 模块的依赖关系

  • 表结构

3.2. 代码分析

  • 前端代码分析

    关于前端代码分享这里就不多叙述了,属性vue3的同学看看就知道了,有些若依自定义的属性,上午查一些或者问AI也很容易明白。感兴趣的同学可以将我们生成的课程模块的前端代码过一遍。

  • 后端代码分析

    我基于前面代码生成器生成的后端代码进行分析(注意我这里文件名和老师有些不一样)

    • CourseController

      接收前端请求,调用service处理逻辑并返回处理后的数据,遵循restful风格

      • 注解

        其中,@Log记录用户操作日志(涉及AOP和异步管理器),@PreAuthorize做++权限校验控制++

      • 查询课程表(分页)

        java 复制代码
        @PreAuthorize("@ss.hasPermi('course:course:list')")
        @GetMapping("/list")
        public TableDataInfo list(TbCourse tbCourse)
        {
            // 1. 开启分页
            startPage();
            // 2. 查询课程列表
            List<TbCourse> list = tbCourseService.selectTbCourseList(tbCourse);
            // 3.返回表格分页数据对象
            return getDataTable(list);
        }
        1. 开始分页

        鼠标左键+Ctrl点击startPage(),找到了所属类BaseController,是web层通用的数据处理类

        CourseController也继承与这个类

        我们按住Alt + 7可以看到整个类的属性方法

        通过以下操作,我们可以找分页的具体处理逻辑

        java 复制代码
        public static void startPage()
        {
            // 1. 创建一个PageDomain的对象,用于接收前端分页的参数
            PageDomain pageDomain = TableSupport.buildPageRequest();
            // 2. 从PageDomain的对象中提取除当前页码、每页大小、排序条件
            Integer pageNum = pageDomain.getPageNum();
            Integer pageSize = pageDomain.getPageSize();
            // 3. 对排序条件进行转义处理,防止SQl注入
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            Boolean reasonable = pageDomain.getReasonable();    // 获取是否合理分页
            // 4. 使用PageHelper开启分页,设置页码、每页大小排序条件、是否合理分页
            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
        }


        2. 查询课程列表

        分页插件的逻辑如下


        3. 返回表格分页数据对象

        返回分页查询的数据

        总结出来,分类查询返回如图

        • 导出课表

          使用了一个wxcel工具类

        • 其他操作(非分页)

          例如获取课程管理详细信息(非分页查询),使用了success对象返回,代码如下

          java 复制代码
          @PreAuthorize("@ss.hasPermi('course:course:query')")
          @GetMapping(value = "/{id}")
          public AjaxResult getInfo(@PathVariable("id") Long id)
          {
              return success(tbCourseService.selectTbCourseById(id));
          }

          使用了(AjaxResult:操作消息提醒)来返回数据,

          其他操作(例如新增),则返回toAjax对象,主要来返回影响数据的调试

          java 复制代码
          @PreAuthorize("@ss.hasPermi('course:course:add')")
          @Log(title = "课程管理", businessType = BusinessType.INSERT)
          @PostMapping
          public AjaxResult add(@RequestBody TbCourse tbCourse)
          {
              return toAjax(tbCourseService.insertTbCourse(tbCourse));
          }
        • BaseEntity

          Entity的基类,其中searchValue为全文检索时,记录前端传递的参数;params为map集合的请求参数,是为了灵活接收参数。

    • CourseService及实现类

      • 接口类

        声明方法定义规范,让实现类完成具体的操作

        其中,新增、修改、删除返回值为int类型,是因为我们在进行数据库操作时,增删改会返回影响的行数,当影响行数大于0,表明操作成功。

      • 实现类

        实现接口,重写所有的抽象方法,调用mapper完成数据库的相关操作,其中添加和修改操作有字段填充字段。

    • CourseMapper及映射文件

      完成对数据从操作

    • Course

      继承BaseEntity基类,和数据库的表字段完成一一对应

  • 前后端交互流程

    • 查询课程管理列表

      查询请求

      前端交互

      反向代理,解决前端路径和后端请求不一致的问题

      在前端项目中的vite.config.js中有以下代码,来进行反向代理(不过这种方式只有在开发模式生效,如果打包为dist部署到服务器,就需要勇nginx来配置反向代理),并对/dev-api进行重写。

      js 复制代码
      server: {
      	proxy: {
              '/dev-api': {
                target: 'http://localhost:8080',
                changeOrigin: true,
                rewrite: (p) => p.replace(/^\/dev-api/, '')
              }
          }
      }

3.3. 权限注解

  • @PreAuthorize 注解是 Spring Security 框架中用来做权限检查的。

  • 它在运行方法前先验证权限,权限够就放行,不够就拦截。

  • 权限控制流程图

4. 二次开发

4.1. 新建业务模块

  • 若依脚手架修改器

    若依框架修改器是一个可以一键修改RuoYi框架包名、项目名等的工具。

    地址:https://gitee.com/lpf_project/RuoYi-MT/releases

    下载好后是一个exe文件,我们将若依项目压缩为zip压缩包,然后双击启动整个exe文件就可以继续包名修改了

    项目修改成功,如果项目能正常运行,就没有什么问题了。

  • 创建外卖管理系统系统模块

    1. 创建子模块

      鼠标右击父工程,新建模块

      • 引入核心模块 (++为了让项目能够使用到framework模块++)

        在admin模块的pom.xml中找到核心模块坐标

        xml 复制代码
        <!-- 核心模块-->
        <dependency>
            <groupId>com.jia</groupId>
            <artifactId>jia-framework</artifactId>
        </dependency>

        在我们创建的模块的pom.xml中,添加

        xml 复制代码
        <!-- 核心模块-->
        <dependencies>
        	<dependency>
        		<groupId>com.jia</groupId>
        		<artifactId>jia-framework</artifactId>
            </dependency>
        </dependencies>
    2. 父工程版本锁定

      在父工程中,对新建的模块进行版本锁定,在父工程的pom.xml中,添加下面配置

      xml 复制代码
      <!-- 商家管理 -->
      <dependency>
          <groupId>com.jia</groupId>
          <artifactId>sky-merchant</artifactId>
          <version>${jia.version}</version>
      </dependency>
    3. sky-admin依赖

      在admin模块的pom.xml中添加下面配置(不需要指定版本了,父工程已经指定)

      xml 复制代码
      <!-- 商家管理 -->
      <dependency>
          <groupId>com.jia</groupId>
          <artifactId>sky-merchant</artifactId>
      </dependency>

4.2. 菜品管理

利用若依代码生成器(主子表模板),生成菜品管理的前后端代码。这一部分很多和入门案例很像,这里就不写那么详细了。

  1. 导入sql

    sql 复制代码
    -- MySQL dump 10.13  Distrib 8.0.31, for Win64 (x86_64)
    --
    -- Host: 127.0.0.1    Database: ry-vue
    -- ------------------------------------------------------
    -- Server version	8.0.31
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!50503 SET NAMES utf8mb4 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    
    --
    -- Table structure for table `tb_dish`
    --
    
    DROP TABLE IF EXISTS `tb_dish`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!50503 SET character_set_client = utf8mb4 */;
    CREATE TABLE `tb_dish` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
      `name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT '菜品名称',
      `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格',
      `image` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '图片',
      `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '描述信息',
      `status` int DEFAULT '1' COMMENT '0 停售 1 起售',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_dish_name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='菜品管理';
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `tb_dish`
    --
    
    LOCK TABLES `tb_dish` WRITE;
    /*!40000 ALTER TABLE `tb_dish` DISABLE KEYS */;
    INSERT INTO `tb_dish` VALUES (30,'干锅牛蛙',38.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','干锅牛蛙',1,'2023-04-13 23:14:12','2023-04-16 22:04:25'),(50,'馒头',1.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','优质面粉',1,'2022-06-10 09:34:28','2023-04-14 09:34:45'),(74,'黑金鲍鱼',68.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','新西兰黑金鲍鱼',1,'2023-02-16 09:48:28','2023-04-13 22:44:08'),(75,'波士顿龙虾',128.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','2斤重 波斯顿龙虾',1,'2023-02-16 09:50:06','2024-05-09 09:53:58'),(76,'香辣烤乌江鱼3斤',108.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','香辣烤乌江鱼3斤',1,'2023-02-16 09:52:30','2023-04-13 22:41:29'),(77,'香辣烤鱼3斤',78.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','香辣烤鱼3斤 草鱼',1,'2023-02-17 15:27:02','2023-04-13 22:39:20'),(80,'宽粉',8.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/c04d2598-0b96-44d7-8ea9-72792b9be66a.jpg','宽粉',0,'2023-04-13 22:48:57','2023-04-15 13:45:03'),(81,'青笋',10.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/3685a52b-46ca-4e2a-8fd2-d90205fa9405.jpg','青笋',1,'2023-04-13 22:49:21','2023-04-13 22:49:21'),(82,'鲜豆皮',8.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/c04d2598-0b96-44d7-8ea9-72792b9be66a.jpg','鲜豆皮',1,'2023-04-13 22:49:52','2023-04-13 22:49:52'),(83,'娃娃菜',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/0bf2be55-5ab3-4e4c-8b1c-98e17b515a7a.jpg','娃娃菜',1,'2023-04-13 22:50:26','2023-04-13 22:50:26'),(84,'土豆片',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','土豆片',1,'2023-04-13 22:50:59','2023-04-13 22:50:59'),(85,'鱼豆腐',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/76e6258a-1df9-4561-b814-39cb9dc56e5e.jpg','鱼豆腐',1,'2023-04-13 22:53:07','2024-04-29 21:42:07'),(86,'重庆毛血旺',58.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/cc5e7eb5-c9f4-48c1-80e9-a167ae100086.jpg','重庆毛血旺',1,'2023-04-13 22:56:18','2023-04-13 22:56:18'),(87,'重庆辣子鸡',48.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/cc5e7eb5-c9f4-48c1-80e9-a167ae100086.jpg','重庆辣子鸡',1,'2023-04-13 22:56:57','2023-04-13 22:56:57'),(88,'渝乡沸腾鱼片',79.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','渝乡沸腾鱼片 草鱼',1,'2023-04-13 22:58:29','2023-04-13 22:58:29'),(89,'皮蛋豆腐',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/df727356-66ba-465c-9da2-c62923d4a1e8.jpg','皮蛋豆腐 鸭蛋 松花江皮蛋',1,'2023-04-13 22:59:45','2023-04-13 22:59:45'),(90,'果仁菠菜',18.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/ca875acb-f103-405f-8379-a6a4c309d6f8.jpg','果仁菠菜 花生 菠菜',1,'2023-04-13 23:01:32','2023-04-13 23:01:32'),(91,'蒜泥黄瓜',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/ca875acb-f103-405f-8379-a6a4c309d6f8.jpg','蒜泥黄瓜 大蒜 剁椒',1,'2023-04-13 23:02:32','2023-04-13 23:02:32'),(92,'老醋花生',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','老醋花生 老陈醋',1,'2023-04-13 23:03:15','2023-04-13 23:03:15'),(93,'干锅花菜',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/3685a52b-46ca-4e2a-8fd2-d90205fa9405.jpg','干锅花菜',1,'2023-04-13 23:10:32','2023-04-13 23:10:32'),(94,'干锅千页豆腐',30.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/76e6258a-1df9-4561-b814-39cb9dc56e5e.jpg','干锅千页豆腐',1,'2023-04-13 23:11:08','2023-04-13 23:11:08'),(95,'干锅鸡胗',38.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/f805ac90-3c62-4795-ad34-d22f83af78ae.jpg','干锅鸡胗',1,'2023-04-13 23:11:46','2023-04-13 23:11:46'),(96,'干锅藕片',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','干锅藕片',1,'2023-04-13 23:13:18','2023-04-13 23:13:18'),(98,'扬州炒饭',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/fb11b202-c8f5-4d9e-9e68-d1d3f13bb3f6.jpg','扬州炒饭',1,'2023-04-13 23:17:52','2023-04-16 22:04:29');
    /*!40000 ALTER TABLE `tb_dish` ENABLE KEYS */;
    UNLOCK TABLES;
    
    --
    -- Table structure for table `tb_dish_flavor`
    --
    
    DROP TABLE IF EXISTS `tb_dish_flavor`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!50503 SET character_set_client = utf8mb4 */;
    CREATE TABLE `tb_dish_flavor` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
      `dish_id` bigint NOT NULL COMMENT '菜品',
      `name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '口味名称',
      `value` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '口味列表',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=198 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='菜品口味关系表';
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `tb_dish_flavor`
    --
    
    LOCK TABLES `tb_dish_flavor` WRITE;
    /*!40000 ALTER TABLE `tb_dish_flavor` DISABLE KEYS */;
    INSERT INTO `tb_dish_flavor` VALUES (1,63,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(111,63,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(112,60,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(113,60,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\"]'),(114,57,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(115,57,'辣度','[\"不辣\",\"微辣\",\"中辣\"]'),(116,56,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(117,54,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(118,52,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(119,52,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(122,73,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(136,77,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(137,77,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(138,76,'辣度','[\"微辣\",\"中辣\"]'),(139,76,'忌口','[\"不要葱\",\"不要香菜\",\"不要辣\"]'),(140,75,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(141,75,'辣度','[\"不辣\",\"中辣\",\"重辣\"]'),(142,74,'忌口','[\"不要葱\",\"不要蒜\"]'),(143,74,'甜味','[\"无糖\",\"少糖\",\"半糖\"]'),(144,86,'辣度','[\"微辣\",\"中辣\",\"重辣\"]'),(145,87,'辣度','[\"微辣\",\"中辣\",\"重辣\"]'),(146,88,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(147,88,'辣度','[\"微辣\",\"中辣\"]'),(148,89,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(149,89,'甜味','[\"无糖\",\"少糖\"]'),(150,90,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(151,90,'辣度','[\"不辣\",\"微辣\"]'),(152,91,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(153,91,'辣度','[\"不辣\",\"微辣\"]'),(154,92,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(155,51,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(156,51,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(157,72,'辣度','[\"微辣\"]'),(159,53,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(160,53,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(161,93,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(162,93,'辣度','[\"微辣\",\"中辣\"]'),(163,94,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(164,94,'辣度','[\"不辣\",\"微辣\"]'),(165,95,'辣度','[\"微辣\",\"中辣\"]'),(166,96,'辣度','[\"微辣\",\"中辣\"]'),(167,97,'忌口','[\"不要蒜\",\"不要香菜\"]'),(168,97,'辣度','[\"微辣\",\"中辣\"]'),(169,67,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(170,66,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(172,65,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(182,85,'甜味','[\"全糖\",\"半糖\",\"少糖\"]');
    /*!40000 ALTER TABLE `tb_dish_flavor` ENABLE KEYS */;
    UNLOCK TABLES;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    -- Dump completed on 2024-08-28 11:11:06
  2. 配置代码生成信息

    • 创建起售、停售的数据字典

    • 生成菜品表代码

      • 菜品表

      • 口味表

  3. 下载代码并导入项目

    下载解压菜品表后目录如图,执行sql语句,导入菜品菜单表

    导入前端代码

    导入后端代码

    遗留的问题

    启动项目如图,但是还是有一些问题

    1. 项目不需要展示主键(售价最好添加人民币标志符,格式化更新时间)

    2. 菜品口味输入是文本框,用户交互不友好(口味名可以优化为文本框,口味标签可以优化成多标签选择器)

    3. 编辑弹框回显时图片不能正常显示(测试数据不能回显,自己添加的数据可以回显)

  • 解决遗留问题

    老师是用ai来解决这些依赖问题的,目的是带大家熟悉AI的使用,但是我AI已经用了很久了,就直接给出修改方案了。

    1. 删除主键

      src\views\merchant\dish\index.vue中,注释相关代码

      html 复制代码
      <!-- <el-table-column label="主键" align="center" prop="id" /> -->

      添加人民币标志符

      html 复制代码
      <el-table-column label="售价" align="center" prop="price">
          <template #default="scope">
      		<span>¥{{ scope.row.price }}</span>
          </template>
      </el-table-column>

      格式化更新时间

      html 复制代码
      <el-table-column label="更新时间" align="center" prop="updateTime" width="180">
          <template #default="scope">
      		<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
          </template>
      </el-table-column>
    2. 口味交互优化

      通过前端抓包,可以看到

      • 将口味名称改为下拉框

        设置口味列表数组(一般在正式项目中,dishFlavorListSelect一般是个空表,数据应该来源于后端接口,而不是写死)

        js 复制代码
        // 定位口味名称和口味列表静态数据
        const dishFlavorListSelect = ref([
          {
            name: "辣度",
            value: ["不辣","微辣","中辣","重辣"]
          },
          {
            name: "忌口",
            value: ["不要葱","不要蒜","不要香菜"]
          },
          {
            name: "甜味",
            value: ["无糖","少糖","半糖","多糖"]
          },
        ])

        150行左右,讲输入框改为下拉框

        html 复制代码
        <template #default="scope">
        	<!-- <el-input v-model="scope.row.name" placeholder="请输入口味名称" /> -->
        	<el-select v-model="scope.row.name" placeholder="请选择口味名称" >
            	<el-option
             	 v-for="dict in dishFlavorListSelect"
             	 :key="dict.value"
             	 :label="dict.name"
             	 :value="dict.name"
             	></el-option> 
            </el-select>
        </template>
      • 口味标签可以优化成多标签选择器

        这里没有用多标签选择器,而是下拉框,下面是创建口味列表数组 更新口味名称时触发的函数

        js 复制代码
        // 存储当前选中口味列表数组
        const checkValueList = ref([])
        // 定义改变口味名称时更新当前选中的口味的列表
        const changeFlavorName = (row) => { 
          // 清空当前选中的口味列表
          row.value = []
          checkValueList.value = dishFlavorListSelect.value.find(item => item.name === row.name).value
        }

        绑定触发函数并将口味列表改为下拉框,其实这里还是有一些bug,像忌口可以多选,但是辣度和甜度只能单选,这个也不难,有兴趣可以自己实现玩玩(这里提供一些思路,从multiple下手,给口味名称添加一个isultiple属性来绑定multiple即可)。

        html 复制代码
        <el-table-column label="口味名称" prop="name" width="150">
            <template #default="scope">
            	<!-- <el-input v-model="scope.row.name" placeholder="请输入口味名称" /> -->
            	<el-select v-model="scope.row.name" placeholder="请选择口味名称"
                       @change="changeFlavorName(scope.row)" >
                	<el-option
                      v-for="dict in dishFlavorListSelect"
                      :key="dict.value"
                      :label="dict.name"
                      :value="dict.name"
                     ></el-option> 
                </el-select>
            </template>
        </el-table-column>
        <el-table-column label="口味列表" prop="value" width="150">
            <template #default="scope">
            	<el-select v-model="scope.row.value" placeholder="请选择口味列表" multiple>
                	<el-option
                      v-for="value in checkValueList"
                      :key="value"
                      :label="value"
                      :value="value"
                     ></el-option> 
                </el-select>
            </template>
        </el-table-column>

        效果如图

      • 解决传输数据不一致问题

        按照上面的代码提交口味修改如图

        这是因为我们前端传输给后端的是数组(而不是后端需要的字符串),我们需要将口味列表的元素序列号

        js 复制代码
        /** 提交按钮 */
        // ...
        if (valid) {
              form.value.dishFlavorList = dishFlavorList.value
        
              // 处理口味列表数组为字符串
              if (dishFlavorList.value === mull) {
                // 处理口味列表数组为字符串
                form.value.dishFlavorList.forEach(item => {
                  item.value = JSON.parse(item.value)
                })
              }
            // ...
        }
      • 解决口味列表不回显问题

        虽然我们将数组序列化为字符串传给后端了,但是后端传给前端的依旧是字符串,如果没有转换,前端自然无法识别

        js 复制代码
        /** 修改按钮操作 */
        // ...
        getDish(_id).then(response => {
            form.value = response.data
            dishFlavorList.value = response.data.dishFlavorList
        
            if (dishFlavorList.value != null) {
              // 处理口味列表数组为字符串
              dishFlavorList.value.forEach(item => {
                item.value = JSON.parse(item.value)
              })
            }
            open.value = true
            title.value = "修改菜品管理"
          })
        }
      • 解决依旧有数据就无法获取焦点

        编写获取焦点方法

        js 复制代码
        // 定义口味列表获取焦点时更新当前选中的口味列表
        const focusFlavorName = (row) => { 
          checkValueList.value = dishFlavorListSelect.value.find(item => item.name === row.name).value
        }

        @focus绑定

        vue 复制代码
        <el-select v-model="scope.row.value" placeholder="请选择口味列表" multiple
            @focus="focusFlavorName(scope.row)">
        </el-select>

        其余什么改样式的画,大家感兴趣就改不,我有点困了。

    3. 图片正常回显

      本地上传的图片若依默认给存到了本地,老师给的测试数据给的是阿里oss的链接,阿里云是完整的资源访问路径,而若依是若依后端配置和数据库存储路径的拼接

      若依的储存地址配置在后端文件sdmin模块的src/main/resources/application.yml中,大概第10行左右,默认地址为D:/ruoyi/uploadPath

      在老师的案例中,请求地址会拼接/profile/upload/xxx,但是我下载的若依版本比较靠后,没有这个问题,但是图片还是没有正常回显。但是,我按照老师的方法修改同样有效

      这个补充一下,如果你不能Ctrl + 鼠标右击调整到imaguoload组件,可以安装组件** vue peek**

      src\components\ImageUpload\index.vue中,的81行左右可以看出baseUrl赋值了vite的环境变量

      js 复制代码
      const baseUrl = import.meta.env.VITE_APP_BASE_API;

      96行左右修改(注意是修改if语句的条件,其中item.indexOf(baseUrl) === -1的意思是:判断变量 item中是否不包含 baseUrl 这个字符串),然后这个问题就解决了

      js 复制代码
      if (item.indexOf(baseUrl) === -1 && item.indexOf("http") === -1) {
      	item = { name: baseUrl + item, url: baseUrl + item };
      }
  • 我遇到的坑

    • 包名不同导致的包扫描问题

      可以看到,我的菜品模块的包名为sky ,而其他模块为jia,这也给我带来了很多问题

      1. MyBatis类型别名不匹配

        在DishMapper.xml中使用了Dish和DishFlavor作为类型别名,但MyBatis的typeAliasesPackage配置只扫描了com.jia. .domain包,而这些类实际上在com.sky.merchant.domain包中。

        • 报错示例

          复制代码
          Failed to parse mapping resource: 'file [E:\Study\20251212230258\jia\sky-merchant\target\classes\mapper\merchant\DishMapper.xml]'
        • 解决方法

          在admin中的application.yml文件中,添加对jia包的别名匹配

          yml 复制代码
          typeAliasesPackage: com.jia.**.domain,com.sky.**.domain
      2. 启动类没有扫描包

        • 报错示例

          JiaApplication.java的@SpringBootApplication注解没有指定扫描com.sky包,导致DishController无法被Spring Boot扫描到(请求报错404)

        • 解决方法

          在JiaApplication.java(启动类)中

          java 复制代码
          @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }, scanBasePackages = {"com.jia", "com.sky"})
    • 数据库表名不对

      • 报错示例

        复制代码
        Field dishMapper in com.sky.merchant.service.impl.DishServiceImpl required a bean of type 'com.sky.merchant.mapper.DishMapper' that could not be found.
        
        The injection point has the following annotations:
        
        - @org.springframework.beans.factory.annotation.Autowired(required=true)
      • 解决方法

        数据库表名和代码生成表名对不上,大家在生成代码时要把表名称改为和数据库表名一样,如果生成好了代码并导入了项目,就修改数据库表明,这样比较快。

4.3. 页面调整

  1. 浏览器标签页icon、标题

    • 标签页icon

      如果没有icon图像,可以在比特虫 https://www.bitbug.net/上转换(我记得好像这个还是很多年前pink老师的课上用到的)

      然后在根目录的index.html中替换link标签的路径即可(或者直接用同名的icon替换favicon.ico的图像),/favicon.ico在public目录下

      html 复制代码
      <link rel="icon" href="/favicon.ico">
    • 标题

      根目录的index.html中

      html 复制代码
      <title>气Π的demo</title>

      但是不生效,这是因为有环境配置文件给覆盖了,如果是开发环境,就修改根目录下的.env.development文件

      复制代码
      # 页面标题
      VITE_APP_TITLE = 气Π的demo

      改好后效果如图

  2. 系统页面中的logo

    替换src\assets\logo\logo.png,图片尺寸改为64 x 64比较合适

  3. 去除源码 & 文档

    src\layout\components\Navbar.vue注释掉相关代码

    html 复制代码
    <!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
    	<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
    </el-tooltip>
    <el-tooltip content="文档地址" effect="dark" placement="bottom">
    	<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
    </el-tooltip> -->
  4. 主题和自定义图标

    • 主题

      在头像下拉框 => 布局设置中修改

    • 自定义图标

      在图标下载网址(注意图标能不能商用),如阿里矢量图标库(iconfont-阿里巴巴矢量图标库)下载svg图标,再复制到前端代码中的src\assets\icons\svg\下面,然后在若依系统的图标设置就可以使用我们刚刚下载的svg图标了

  5. 登录页面中标题、背景图

    • 标题

      src\views\login.vue中,替换下面即可

      html 复制代码
      <h3 class="title">气Π的demo</h3>
    • 背景图

      src\views\login.vue中可以看到,背景图片路径在src\assets\images\login-background.jpg中,替换即可。

5. 打包发布

5.1. 后端项目的打包发布

  1. 修改端口

    在admin模块的application.yml中修改后端端口

    还是这个文件,把redis配置页改一下


    2. 修改数据库连接

    在application-druid.yml中修改数据库连接

  • 打包后端文件

    双击生命周期的package

    生成目录的jar路径,会在打包时会在控制台打印出来

    在这个目录下运行java -jar jia-admin.jar,下面有RY的图标就代表打包的jar包能做出运行

  • 发布后端项目

    1. 上传项目

      将项目上传到宝塔文件的/www/wwwroot/xxx文件下面

    2. 创建项目

      创建为java项目并添加jdk信息(你可以下载宝塔里面的jdk,而可以上传自己的jdk)

    3. 解决日志路径不匹配问题

      然后项目报错如图,分析了一下大概是说我服务器没有若依配置中的日志存储路径

      就是我们在admin模块的logback.xml的日志存放路径设置,你可以在服务器创建/home/ruoyi/logs这个路径,或者改为服务器已经存在的路径

      yml 复制代码
      <!-- 日志存放路径 -->
      <property name="log.path" value="/home/ruoyi/logs" />

      重新启动项目,并能启动成功,并能访问服务器的swagger文档http://your-sever:8002/swagger-ui/index.html,就代表后端项目发布成功(在开发阶段可以开发测试文档的访问,正式部署记得关闭)

5.2. 前端项目的打包发布

  • 修改测试环境的后端服务路径

    在前端根目录下的vite.config.js文件,将your-server改为你的后端服务器地址(如果端口部署8002),的话,也可以改,但是前面也说了,这是这个配置只有在测试环境生效

    js 复制代码
    server: {
      port: 80,
      host: true,
      open: true,
      proxy: {
         // https://cn.vitejs.dev/config/#server-proxy
         '/dev-api': {
          	target: 'http://your-server:8002',
          	// target: 'https://api.wzs.pub/mock/13',
          	changeOrigin: true,
          	rewrite: (p) => p.replace(/^\/dev-api/, '')
         }
      }
    },
  • 不同的环境

    在前端项目根目录下,有如下环境配置文件

    其中.env.development内容如图,开发环境的地址为/dev-api,被我们上面的vite.config.js代理转发了

    复制代码
    # 页面标题
    VITE_APP_TITLE = 气Π的demo
    
    # 开发环境配置
    VITE_APP_ENV = 'development'
    
    # 若依管理系统/开发环境
    VITE_APP_BASE_API = '/dev-api'
    • 生产环境和预发布 / 测试的区别

      对比维度 .env.production(生产环境) .env.staging(预发布 / 测试环境)
      环境标识 VITE_APP_ENV = 'production' VITE_APP_ENV = 'staging'
      API 基础路径 VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API = '/stage-api'
      构建命令 npm run build(默认加载该配置) npm run build:stage(通过 --mode staging 加载)
      核心作用场景 正式部署上线,面向最终用户使用 预发布测试、功能验证、环境适配测试
      配置应用范围 生产环境 API 连接、正式部署 URL 路径配置 预发布环境 API 连接、测试环境 URL 路径配置
  • 修改测试环境的后端服务路径

    我们这里就直接用生产环境打包了,配置.env.production,将下面的/prod-api改为http/https + your-server

    复制代码
    # 若依管理系统/生产环境
    VITE_APP_BASE_API = '/prod-api'

    然后运行打包命令npm run build,将项目的打包文件(项目根目录下的dist文件夹),放到宝塔文件夹中,

    然后创建php站点如下,域名可以选择IP地址格式或者已经解析的域名,根目录选择上面的dist文件夹,下面的配置不用改

  • 解决内容混合问题

    一般的同学用IP地址格式解析配置的话不会遇到这个问题,但如果你绑定了SSL证书和域名,而且http和https混用就会导致内容混合问题,浏览器控制台报错如下

    这玩意就有点复杂了,但是我相信看到这里的各位基本上都有点东西了,我又就不藏着掖着了

    1. 首先,你得需要域名是吧,取阿里或腾讯注册一个,然后进行子域名划分(域名云解析),如图(记录值就是你的服务器的主机地址)

    2. 然后对前后端进行域名配置(要和你上面域名划分的要一样)

    3. 绑定了域名,就可以申请免费的Lets Encrypt证书,然后勾选域名就可以申请了,很简单

      申请好证书后可以开启强制https访问

      然后者时候就有人会问了,Lets Encrypt证书有效期只有三个月,到期了怎么办,有的孩子,有的,宝塔有任务计划,可以自动执行证书续签脚本,只有执行周期小于三个月就可以实现永久续杯。

    4. 外网映射

      这个在后端项目(我们这里是java)中才有,开启的前提是后端项目配置域名。它的功能有的像反向代理,可以转发重写,没有特殊要求的话就直接写/(域名请求全部映射到8002端口)

      然后改一下前端打包环境的请求路径为域名,后端配置一下跨域设置,就跨域成功运行项目啦。

相关推荐
深蓝海拓2 小时前
PySide6从0开始学习的笔记(三) 布局管理器与尺寸策略
笔记·python·qt·学习·pyqt
暗然而日章2 小时前
C++基础:Stanford CS106L学习笔记 8 继承
c++·笔记·学习
2401_834517072 小时前
AD学习笔记-34 PCBlogo的添加
笔记·学习
阿里云云原生2 小时前
AgentScope Java 1.0:从模型到应用,AI Agent 全生命周期管理利器!
java·云原生
被考核重击2 小时前
浏览器原理
前端·笔记·学习
running up2 小时前
Maven依赖管理和项目构建工具
java·maven
Lynnxiaowen2 小时前
今天我们继续学习kubernetes内容Helm
linux·学习·容器·kubernetes·云计算
charlie1145141912 小时前
编写INI Parser 测试完整指南 - 从零开始
开发语言·c++·笔记·学习·算法·单元测试·测试
冬夜戏雪3 小时前
【java学习日记】【12.14】【12/60】
学习