学习瑞吉
软件开发整体介绍
软件开发流程
①需求分析:产品原型,需求规格说明书
②设计:产品文档,UI界面设计,概要设计,详细设计,数据库设计
③编码:项目代码,单元测试
④测试:测试用例,测试报告
⑤上线运维:软件环境安装,配置
角色分工
①项目经理:对整个项目负责,任务分配,把控进度
②产品经理:进行需求调研,输出需求调研文档,产品原型等
③UI设计师:根据产品原型输出界面效果图
④架构师:项目整体架构设计,技术选型等
⑤开发工程师:代码实现
⑥测试工程师:编写测试用例,输出测试报告
⑦运维工程师:软件环境搭建,项目上线
软件环境
①开发环境(development):开发人员在开发阶段使用的环境,一般外部用户无法访问
②测试环境(testing):专门给测试人员使用的环境,用于测试项目,一般外部用户无法访问
③生产环境(production):即线上环境,正式提供对外服务的环境
瑞吉外面项目介绍
项目介绍
本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产件 ,包括系统管理后台和移动端应用两部分。其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的菜品、套餐、订单等进行管理维护。移动端应用。主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。
本项目共分为3期进行开发:
第一期主要实现基本需求,其中移动端应用通过Html5实现,用户可以通过手机浏览器访问。
第二期主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便。
第三期主要针对系统进行优化升级,提高系统的访问性能。
产品原型展示
主要展示项目的功能
技术选型
用户层:Html5,VUE.js,ElementUI,微信小程序
网关层:Nginx
应用层:SpringBoot,SpringMVC,SpringSession,Spring,Swagger,lombok
数据层:Mysql,Mybatis,MybatisPlus,Redis
工具:git,maven,junit
功能架构
移动端前台(H5,微信小程序):手机号登录,微信登录,地址管理,历史订单,菜品规格,购物车,下单,菜品浏览
系统管理后台:分类管理,菜品管理,套餐管理,菜品口味管理,员工登录,员工退出,员工管理,订单管理
角色
后台系统管理员:登录后台管理系统,拥有后台系统肿的所有操作权限
后台系统普通员工:登录后台管理系统,对菜品,套餐,订单进行管理
C端用户:登录移动端应用,可以浏览产品,添加购物车,设置地址,在线下单等
开发环境搭建
数据库环境搭建
命令行模式:
create database reggie character set utf8mb4; //创建reggie数据库并设置编码
source 路径指向sql文件; //导入sql文件
导入的数据表:
employee:员工表
category:菜品和套餐分类表
dish:菜品表
setmeal:套餐表
setmeal_dish:套餐菜品关系表
dish_flavor:菜品口味关系表
user:用户表(C端)
address_book:地址薄表
shopping_cart:购物车表
orders:订单表
orders_detail:订单明细表
maven项目搭建
后台登录功能开发
需求分析
代码开发
①创建实体类Employee,和employee对应
②创建controller,service,mapper层
③简单对类和接口进行初始化一下
④导入返回结果类,此类是一个通用结果类,服务器响应的所有结果最终都会包装成此种类型返回给前端页面
⑤在Controller中创建登录方法
处理逻辑如下:
1.将页面提交的密码password进行md5加密处理
2.根据页面提交的用户名username查询数据库
3、如果没有查询到则返回登录失败结果
4、密码比对,如果不一致则返回登录失败结果
5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
6、登录成功,将员工id存入Session并返回登录成功结果
功能测试
后台退出功能开发
需求分析
代码开发
用户点击页面中退出按钮,发送请求,请求地址为/employee/logout,请求方式为post,我们只需要在controller中创建对应的处理方法即可,具体的处理逻辑:
①清理Session中的用户id
②返回结果
功能测试
员工管理功能开发
完善登录功能
问题分析
前面我们已经完成了后台系统的员工登录功能开发,但是还存在-一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。
实现:答案就是使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。
代码实现:
①创建自定义过滤器LoginCheckFilter
②在启动类上加入注解@ServletComponentScan
③完善过滤器的处理逻辑
过滤器具体的处理逻辑如下:
1.获取本次请求的URI
2.判断本次请求是否需要处理
3.如果不需要处理,则直接放行
4.判断登录状态,如果已登录,则直接放行
5.如果未登录则返回未登录结果
功能测试
新增员工功能
需求分析
后台系统中在员工管理中,可以进行员工的增加等
数据模型
新增员工,其实就是将我们新增页面录入的员工数据插入到employee表。需要注意,employee表中 对username字段加入了唯一约束,因为username是员工的登录账号,必须是唯一的
代码实现
在开发代码之前,需要梳理一下整个程序的执行过程:
1.页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
2.服务端Controller接收页面提交的数据并调用Service将数据进行保存
3. Service调用Mapper操作数据库,保存数据
前面的程序还存在一个问题, 就是当我们在新增员工时输入的账号已经存在,由于employee表中对该字段加入了唯一约束,此时程序会抛出异常:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'zhangsan' for key 'idx_username'
此时需要我们的程序进行异常捕获,通常有两种处理方式:
1.在Controller方法中加入try. catch进行异常捕获
2.使用异常处理器进行全局异常捕获(推荐)
功能测试
总结:
1、根据产品原型明确业务需求
2、重点分析数据的流转过程和数据格式
3、通过debug断点调试跟踪程序执行过程
员工信息分页查询
需求分析
系统中的员工很多的时候,如果在-一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
代码开发
在开发代码之前,需要梳理一下整个程序的执行过程:
1.页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端
2.服务端Controller接收页面提交的数据并调用Service查询数据
3. Service调用Mapper操作数据库,查询分页数据
4. Controller将查询到的分页数据响应给页面
5、页面接收到分页数据并通过ElementUl的Table组件展示到页面上
功能测试
启用/禁用员工账号
需求分析
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示。
管理员admin登录系统可以对所有员工账号进行启用、禁用操作。
如果某个员工账号状态为正常,则按钮显示为“禁用”,如果员工账号状态为已禁用,则按钮显示为“启用”
代码开发
在开发代码之前,需要梳理一下整个程序的执行过程:
1、页面发送ajax请求,将参数(id、status)提交到服务端
2.服务端Controller接收页面提交的数据并调用Service更新数据
3. Service调用Mapper操作数据库
功能测试
测试过程中没有报错,但是功能没有实现,数据库的数据也没有变化,发现是点击禁用时传的id和数据库不一致,是因为js精度的缺失
代码修复
我们可以在服务端给页码响应json数据时进行处理,将long型数据统一转为String字符串
具体实现步骤:
1)提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象 到json数据的转换(资料中已经提供,直接复制到项目中使用)
2)在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换8中使用提供的对象转换器进行Java对象到json数据的转换
编辑员工信息
需求分析
在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击保存按钮完成编辑操作
代码开发
在开发代码之前需要梳理一下操作过程和对应的程序的执行流程:
1、点击编辑按钮时,页面跳转到add.html, 并在url中携带参数[员工id]
2、在add.html页面获取url中的参数[员工id]
3.发送ajax请求,请求服务端,同时提交员工id参数
4、服务端接收请求,根据员工id查询员工信息,将员工信息以json形式响应给页面
5.页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显
6、点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端
7.服务端接收员工信息,并进行处理,完成后给页面响应
8、页面接收到服务端响应信息后进行相应处理
功能测试
分类管理功能
公共字段自动填充
问题分析
前面我们已经完成了后台系统的员工管理功能开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多表中都有这些字段。
能不能对于这些公共字段在某个地方统一处理, 来简化开发呢?
答案就是使用Mybatis Plus提供的公共字段自动填充功能。
代码实现
Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。
实现步骤:
1、在实体类的属性上加入@TableField注解,指定自动填充的策略
2.按照MP框架要求编写元数据对象处理器,在此类中统一为公共 字段赋值,此类需要实现MetaObjectHandler接口
功能测试
功能完善
前面我们已经完成了公共字段自动填充功能的代码开发,但是还有一个问题没有解决,就是我们在自动填充CreateUser和updateUser时设置的用户id是固定值,现在我们需要改造成动态获取当前登录用户的id。
有的同学可能想到,用户登录成功后我们将用户id存入了HttpSession中,现在我从HttpSession中获取不就行了?
注意,我们在MyMetaObjectHandler类 中是不能获得HtpSession对象的,所以我们需要通过其他方式来获取登录用户id
可以使用ThreadLocal来解决此问题,它是JDK中提供的一个类。
在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一一个线程:
1. LoginCheckFilter的doFilter方法
2. EmployeeController的update方法
3、MyMetaObjectHandler的updateFil方法
可以在上面的三个方法中分别加入下面代码(获取当前线程id) :
long id = Thread.currentThread.getId();
log.info("线程id: {}", id);
执行编辑员工功能进行验证,通过观察控制台输出可以发现,一次请求对应的线程id是相同的:
什么是ThreadLocal?
ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
ThreadLocal常用方法:
public void set(T value):设置当前线程的线程局部变量的值
public T get():返回当前线程所对应的线程局部变量的值
我们可以在LoginCheckilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandlert的updatlill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。
实现步骤:
1、编写BaseContext工具炎, 基于ThreadLocal封装的工具类
2.在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
3.在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id
新增分类功能
需求分析
后台系统中可以管理分类信息,分类包括两种类型,分别是菜品分类和套餐分类。当我们在后台系统中添加菜品时需要选择一个菜品分类,当我们在后台系统中添加一个套餐时需要选择一个套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐。
数据模型
新增分类,其实就是将我们新增窗口录入的分类数据插入到category表
代码开发
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类Category (直接从课程资料中导入即可)
●Mapper接口CategoryMapper
●业务层接口CategoryService
●业务层实现类CategoryServicelmpl
●控制层CategoryController
在开发代码之前,需要梳理一下整个程序的执行过程:
1.页面(backend/page/category/list.html)发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端
2.服务端Controller接收页面提交的数据并调用Service将数据进行保存
3、Service调用Mapper操作数据库,保存数据
可以看到新增菜品分类和新增套餐分类请求的服务端地址和提交的json数据结构相同,所以服务端只需要提供一个方法统一处理即可
功能测试
分类信息分页查询
需求分析
系统中的分类很多的时候,如果全部展示会比较乱,不便于查看,因此在这里我们使用分页
代码开发
在开发代码之前,需要梳理一-下整个程序的执行过程:
1.页面发送ajax请求,将分页查询参数(page. pageSize)提 交到服务端
2.服务端Controller接收页面提交的数据并调用Service查询数据
3、Service调 用Mapper操作数据库,查询分页数据
4. Controller将查询到的分页数据响应给页面
5.页面接收到分页数据并通过ElementUl的Table组件展示到页面上
功能测试
删除分类功能
需求分析
在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。
代码开发
在开发代码之前,需要梳理一下整个程序的执行过程:
1.页面发送ajax请求,将参数(id)提交到服务端
2.服务端Controller接收页面提交的数据并调用Service删除数据
3、Service调用Mapper操作数据库
功能测试
功能完善
前面我们已经实现了根据id删除分类的功能,但是并没有检查删除的分类是否关联了菜晶或者套餐,所以我们需要进行功能完善。
要完善分类删除功能,需要先准备基础的类和接口:
1.实体类Dish和Setmeal (从课程资料中复制即可)
2. Mapper接口DishMapper和SetmealMapper
3、Service接口DishService和SetmealService
4、Service实现类DishServicelmpl和SetmealServicelmpl
修改分类功能
需求分析
在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作
代码开发
功能测试
菜品管理页面
文件上传下载
文件上传接收
文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。
文件上传在项目中应用非常广泛,我们经常发微博,发微信朋友圈都用到了文件上传功能。
文件.上传时,对页面的form表单有如下要求:
●method=” post” //采用post方式提交数据
●enctype=”multipart/form-data” //采用multipart格式上传文件
●type=file” //使用input的file控件上传
目前一些前端组件库也提供了相应的.上传组件,但是底层原理还是基于form表单的文件上传。例如ElementUl中提供的upload上传组件:
服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
●commons-fileupload
●commons-io
Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MutipartFile类型的参数即可接收上传的文件,例如:
代码实现
文件下载介绍
文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程。
通过浏览器进行文件下载,通常有两种表现形式:
●以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
●直接在浏览器中打开
通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。
代码实现
新增菜品
需求分析
后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。
数据模型
新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。
所以在新增菜品时,涉及到两个表:
dish 菜品表
dish_flavor 菜品口味表
代码开发
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类DishFlavor(直接从课程资料中导入即可,Dish实体前面课程中已经导入过了)
●Mapper接口DishFlavorMapper
●业务层接口DishFlavorService
●业务层实现类DishFlavorServicelmpl
●控制层DishController
在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程:
1、页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
2、页面发送请求进行图片上传,请求服务端将图片保存到服务器
3、页面发送请求进行图片下载,将上传的图片进行回显
4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。
注意:在这里接收参数的时候,要注意前端请求的格式,在这里我们使用DTO对象进行传输,也就是这个实体类封装有两个实体类的参数
DTO,全称为Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输。
VO是后端返回给前端的数据
功能测试
菜品信息分页查询
需求分析
代码开发
在开发代码之前,需要梳理一下菜品分页查询时前蟎页面和服务端的交互过程:
1.页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pagesize、name)提交到服务端,获取分页数据
2.页面发送请求,请求服务端进行图片下载,用于页面图片展示
开发菜品信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
功能测试
修改菜品功能
需求分析
在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击确定按钮完成修改操作
代码开发
在开发代码之前,需要梳理一下修改菜晶时前端页面(add.html)和服务端的交互过程:
1.页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示(在前面新增菜品已经完成)
2.页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
3、页面发送请求,请求服务端进行图片下载,用于页图片回显
4、点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
开发修改菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。
功能测试
套餐管理业务开发
新增套餐
需求分析
套餐就是菜品的集合。后台系统中可以管理套餐信息,通过新增套餐功能来添加一个新的套餐,在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品,井且需要上传套餐对应的图片,在移动端会按照套餐分类来展示对应的套餐。
数据模型
新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal_dish表插入套餐和菜品关联数据。所以在新增套餐时,涉及到两个表:
●setmeal 套餐表
●setmeal_dish 套餐菜品关系表
代码开发
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类SetmealDish(直接从课程资料中导入即可,Setmeal实体前面课程中已经导入过了)
●DTO SetmealDto(直接从课程资料中导入即可)
●Mapper接口 SetmealDishMapper
●业务层接口SetmealDishService
●业务层实现类SetmealDishServicelmpl
●控制层SetmealController
在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程:
1、页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
2.页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中
3.页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
4、页面发送请求进行图片上传,请求服务端将图片保存到服务器
5、页面发送请求进行图片下载,将上传的图片进行回显
6、点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端
开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可。
功能测试
套餐信息分页查询
需求分析
代码开发
在开发代码之前,需要梳理一下套餐分页查询时前端页面和服务端的交互过程:
1、页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示
开发套餐信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
功能测试
删除套餐功能
需求分析
代码开发
在开发代码之前,需要梳理一下删除套餐时前端页面和服务端的交互过程:
1、删除单个套餐时,页面发送ajax请求,根据套餐id删除对应套餐
2.删除多个套餐时,页面发送ajax请求,根据提交的多个套餐id删除对应套餐
开发删除套餐功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
观察删除单个套餐和批量删除套餐的请求信息可以发现,两种请求的地址和请求方式都是相同的,不同的则是传递的id个数,所以在服务端可以提供一个方法来统一处理。
功能测试
短信业务开发
短信发送功能
介绍
目前市面上有很多第三方提供的短信服务,这些第三方短信服务会和各个运营商(移动、联通、电信)对接,我们只需要注册成为会员并且按照提供的开发文档进行调用就可以发送短信。需要说明的是,这些短信服务-般都是收费服务。
阿里云短信服务
阿里云短信服务(Short Message Service)是广大企业客户快速触达手机用户所优选使用的通信能力。调用API或用群发助手,即可发送验证码、通知类和营销类短信;国内验证短信秒级触达,到达率最高可达99%;国际/港澳台短信覆盖200多个国家和地区,安全稳定,广受出海企业选用。
应用场景:验证码,短信通知,推广短信
代码开发
步骤:①导入maven坐标
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>2.1.0</version>
</dependency>
②调用API
手机验证码登录功能
需要分析
为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能。
手机验证码登录的优点:
●方便快捷,无需注册,直接登录
●使用短信验证码作为登录凭证,无需记忆密码
●安全
登录流程:
输入手机号>获取验证码>输入验证码>点击登录>登录成功
注意:通过手机验证码登录,手机号是区分不同用户的标识。
数据模型
通过手机验证码登录时,涉及的表为user表,即为用户表
代码开发
在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:
1.在登录页面(front/ page/login.html)输入手机号,点击l获取验证码1按钮,页面发送ajax请求, 在服务端调用短信服务API给指定手机号发送验证码短信
2.在登录页面输入验证码,点击登录按钮,发送ajax请求,在服务端处理登录请求
开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类User (直接从课程资料中导入即可)
●Mapper接口UserMapper
●业务层接口UserService
●业务层实现类UserServicelmpl
●控制层UserController
●工具类SMSUtils, ValidateCodeUtils(直接从课程资料中导入即可)
前面我们已经完成了LoginCheckFilter过滤器的开发,此过滤器用于检查用户的登录状态。我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。
在LoginCheckFilter过滤器中扩展逻辑,判断移动端用户登录状态:
//4-2.判断登录状态,如果已登灭,则直接放行
if (request.getSession().getAttribute("user") != null) {
log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));
Long userId = (Long) request.getSession().getAttribute("user");
BaseContext.setCurrentId(userId);
filterChain.doFilter(request,response);
return;
}
导入用户地址薄功能
需求分析
地址簿指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址。
数据模型
用户的地址信息会存储在address_book表,即地址簿表中。
导入功能代码
功能代码清单:
●实体类AddressBook(直接从课程资料中导入即可)
●Mapper接口AddressBookMapper
●业务层接口AddressBookService
●业务层实现类AddressBookServicelmpl
●控制层AddressBookController(直接从课程资料中导入即可)
功能测试
菜品展示功能
需求分析
用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示选择规格按钮,否则显示+ 按钮。
代码开发
在开发代码之前,需要梳理一下前端页面和服务端的交互过程:
1.页面(front/index.html)发送ajax请求,获取分类数据( 菜品分类和套餐分类)
2.页面发送ajax请求,获取第一个分类下的菜品或者套餐
开发菜品展示功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
注意:首页加载完成后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时修改一下,从
静态json文件获取数据,等后续开发购物车功能时再修改回来,如下:
就是从请求的方法里面把url改一下
功能测试
购物车功能
需求分析
移动端用户可以将菜品或者套餐添加到购物车。对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车;对于套餐来说,可以直接点击①将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量,也可以清空购物车。
数据模型
购物车对应的数据表为shopping_cart表
代码开发
在开发代码之前,需要梳理一下购物车操作时前端页面和服务端的交互过程:
1.点击(加入购物车) 或者 + 按钮,页面发送ajax请求,请求服务端,将菜品或者套餐添加到购物车
2.点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
3、点击清空购物车按钮,页面发送ajax请求,请求服务端来执行清空购物车操作
开发购物车功能,其实就是在服务端编写代码去处理前端页面发送的这3次请求即可。
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类ShoppingCart (直接从课程资料中导入即可)
●Mapper接口 ShoppingCartMapper
●业务层接口ShoppingCartService
●业务层实现类ShoppingCartServicelmpl
●控制层ShoppingCartController
功能测试
用户下单功能
需求分析
移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的去结算按钮,页面跳转到订单确认页面,点击去支付按钮则完成下单操作。
数据模型
用户下单业务对应的数据表为orders表和order_ detail表:
●orders:订单表
●order_detail: 订单明细表
代码开发
在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:
1、在购物车中点击去结算按钮,页面跳转到订单确认页面
2.在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址
3、在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据
4、在订单确认页面点击去支付按钮,发送ajax请求,请求服务端完成下单操作
开发用户下单功能,其实就是在服务端编写代码去处理前端页面发送的请求即可。
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类Orders、OrderDetail (直接从课程资料中导入即可)
●Mapper接口 OrderMapper、 OrderDetailMapper
●业务层接口OrderService、 OrderDetailService
●业务层实现类OrderServicelmpl、OrderDetailServicelmpl
●控制层OrderController、OrderDetailController
功能测试
缓存优化
环境搭建
导入maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置
spring
redis:
host: 172.17.2.94
port: 6379
password: root@123456
database: 0
配置类(这里设置主要是为了设置序列化器)
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//默认的Key序列化器为: JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
缓存短信验证码
实现思路
前面我们已经实现了移动端手机验证码登录,随机生成的验证码我们是保存在HttpSession中的。现在需要改造为将验证码缓存在Redis中,具体的实现思路如下:
1.在服务端UserController中注入RedisTemplate对象,用于操作Redis
2.在服务端UserController的sendMsg方法中,将随机生成的验证码缓存到Redis中,并设置有效期为5分钟
3、在服务端UserController的login方法中,从Redis中获取缓存的验证码,如果登录成功则删除Redis中的验证码
代码改造
功能测试
缓存菜品数据
实现思路
前面我们已经实现了移动端菜品查看功能,对应的服务端方法为DishController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高井发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能。
具体的实现思路如下:
1.改造DihContoller的list方法 ,先从Redis中获取菜品数据,如果有则直接返回,无需查询数据库;如果没有则查询数据库,并将查询到的菜品数据放入Redis。
2.改造DishController的save和update方法,加入清理缓存的逻辑(一种清楚所有,一种精确清理)
注意:在使用缓存过程中,要注意保证数据库中的数据和缓存中的数据一致,如果数据库中的数据发生变化,需要及时清理缓存数据。
SpringCache
介绍
Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓
存技术。
CacheManager是Spring提供的各种缓存技术抽象接口。
针对不同的缓存技术需要实现不同的CacheManager:
CacheManager 描述
EhCacheCacheManager:使用EhCache作为缓存技术
GuavaCacheManager:使用Google的GuavaCache作为缓存技术
RedisCacheManager:使用Redis作为缓存技术
常用注解
@EnableCaching:开启缓存注解功能(放在启动类上)
@Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中(一般放在查询方法上)
常用属性
value:缓存的名称,每个缓存名称下面可以有多个key
key:缓存的key,一般都是动态获取,从参数中去取
condition:条件,满足条件时才缓存数据(他不能用root和result去取)
unless:满足条件则不缓存
@CachePut:将方法的返回值放到缓存中(一般放在添加方法上)
常用的属性
value:缓存的名称,每个缓存名称下面可以有多个key
key:缓存的key,一般都是动态获取,从参数中去取
//如key="#user.id",这种#获取的表达式叫做spel
@CacheEvict:将一条或多条数据从缓存中删除(一般放在删除更新方法上)
常用属性
value:缓存的名称,每个缓存名称下面可以有多个key
key:缓存的key,一般都是动态获取,从参数中去取
//如key="#p0",这里p代表参数的意思,0代表第一个,也就是第一个参数,result代表从返回值中取
allEntries = true:表示执行方法时删除所有缓存数据
注意:在springboot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。
例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。
注意:如果都没有用默认缓存到map集合中,并且是保存到内存里面重启服务缓存就会消失
入门案例(使用redis)
①导入坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
②配置文件
spring:
redis:
host: 192.168.217.128
port: 6379
password: 123456
database: 0
cache:
redis:
time-to-live: 1800000 #设置缓存过期时间,可选
在启动类添加注解
缓存套餐数据
实现思路
前面我们已经实现了移动端套餐查看功能,对应的服务端方法为SetmealController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能。
具体的实现思路如下:
1、导入Spring Cache和Redis相关maven坐标
2、在application.yml中配置缓存数据的过期时间
3、在启动类上加入@EnableCaching注解,开启缓存注解功能
4、在SetmealController的list方法上加入@Cacheable注解
5、在SetmealController的save和delete方法上加入CacheEvict注解
代码改造
Mysql主从复制
概念:
MySQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的二进制日志功能。就是一台或多台MySQL数据库(slave, 即从库)从另一台MySQL数据库(master,即主库)进行日志的复制然后再解析日志并应用到自身,最终实现从库的数据和主库的数据保持一致。MySQL主从复制是MySQL数据库自带功能,无需借助第三方工具。
MySQL复制过程分成三步:
●master将改变记录到二进制日志(binary 1og),然后传到slave中的I/O thread,这个过程需要创建一个用户进行操作
●slave通过I/O thread将master的binary 1og拷贝到它的中继日志(relay 1og)
●然后slave将他解析为SQL thread重做中继日志中的事件,将改变应用到自己的数据库中
环境配置:
①提前准备好两天服务器,分别安装mysql并启动服务成功
主库Master 192.168.217.128
从库Master 192.168.217.128
②配置主库
修改mysql数据库的配置文件/etc/my.cnf
[mysqld]
log-bin=mysql-bin #[必须]启用二进制日志
server-id=100 #[必须]服务器唯一ID
重启mysql服务
systemctl restart mysqld
第三步:登录Mysql数据库,执行下面SQL
GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'root@123456';
注意:上面SQL的作用是创建一个用户xiaoming,密码为root@123456, 并且给xiaoming用户授予REPLICATION SLAVE权限。常用于建立复制时所需要用到的用户权限,也就是slave必须被master授权具有该权限的用户,才能通过该用户复制。
第四步:登录Mysql数据库,执行下面SQL,记录下结果中File和Position的值
show master status;
注意:上面sql的作用是查看master的状态,执行完这个sql以后不要再执行任何操作,因为前面file和position会变,这俩会在从库配置用到
③配置从库
第一步:修改Mysq1数据库的配置文件/etc/my.nf
[mysq1d]
server-id=129 #[必须]服务器唯一ID
重启mysql服务
systemctl restart mysqld
第三步:登录Mysql数据库,执行下面SQL
change master to master_host='192.168.217.128',master_user='xiaoming',master_password='root@123456',master_log_file='mysql-bin.000001',master_log_pos=441;
start slave;
第四步:登录Mysq1数据库,执行下面SQL,查看从数据库的状态
show slave status\G;
#当他们的Slave_IO_Running和Slave_SQL_Running都为yes才算成功
注意:如果Slave_IO_Running不是yes的话,在这里注意克隆的机器他们的mysql的sever-uuid是相等的记得修改,查询 show variables like 'datadir'; 找到地址后使用select uuid(); #生成一个uuid,然后cd /var/lib/mysql/ sudo vim auto.cnf 进行修改即可
读写分离
背景:面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。
Sharding-JDBC
介绍:Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。使用Sharding-JDBC可以在程序中轻松的实现数据库读写分离。
●适用于任何基于JDBC的ORM框架, 如: JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
●支持任何第三方的数据库连接池,如: DBCP, C3P0, BoneCP, Druid, HikariCP等。
●支持任意实现JDBC规范的数据库。目前支持MySQL, Oracle, SQLServer, PostgreSQL以及 任何遵循SQL92标准的数据库。
入门案例
使用Sharding-JDBC实现读写分离步骤:
1.导入maven坐标
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
2.在配置文件中配置读写分离规则并配置允许bean定义覆盖配置项
server:
port: 8080
mybatis-plus:
configuration:
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: ASSIGN_ID
spring:
shardingsphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.217.128:3306/rw?characterEncoding=utf-8
username: root
password: hadoop
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.217.129:3306/rw?characterEncoding=utf-8
username: root
password: hadoop
masterslave:
# 读写分离配置
load-balance-algorithm-type: round_robin #轮询
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个逗号分隔
slave-data-source-names: slave
props:
sql:
show: true #开启SQL显示,默认false
main:
allow-bean-definition-overriding: true # 允许bean定义进行覆盖
项目实现读写分离
数据库环境准备(主从复制)
直接使用我们前面在虚拟机中搭建的主从复制的数据库环境即可。
在主库中创建瑞吉外卖项目的业务数据库reggie并导入相关表结构和数据。
在项目中加入Sharding-JDBC实现读写分离步骤:(同上)
1、导入maven坐标
2.在配置文件中配置读写分离规则
3、在配置文件中配置允许bean定义覆盖配置项
Nginx
概述:
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx的网站有:百度、京东、新浪、网易、腾讯、淘宝等。官网: https://nginx.org/
安装过程:
1.安装依赖包yum -y install gcc pcre-devel zlib-devel openssl openssl-devel zlib zlib-devel
2、下载Nginx安装包wget https://nginx.org/download/nginx-1.16.1.tar.gz
3、解压tar -zxvf nginx-1.16.1.tar.gz
mkdir -p /usr/local/nginx
4、cd nginx-1.16.1
5、./configure --prefix=/usr/local/nginx
6、make && make install
目录结构
重点目录/文件:
●conf/nginx.conf:nginx配置文件
●html:存放静态文件(html. CsS、 Js等)
●logs:日志目录,存放日志文件
●sbin/nginx:二进制文件,用于启动、停止Nginx服务
nginx命令
查看版本:./nginx -v(进入到sbin目录,下面都是)
检查配置文件是否有错:./nginx -t
启动Nginx服务使用如下命令:./nginx
停止Nginx服务使用如下命令:./nginx -s stop
启动完成后可以查看Nginx进程:ps -ef|grep nginx
重新加载配置文件:./nginx -s reload
注意:在浏览器访问http://192.168.217.128:80,即可查看是否启动成功
环境变量配置
①vim /etc/profile
②进去以后直接在path后面追加/usr/local/nginx/sbin:
#这里加:是因为后面还有java等的路径
③source /etc/profile
配置文件的结构
Nginx配置文件(conf/nginx.conf)整体分为三部分:
●全局块:和Nginx运行相关的全局配置
●events块:和网络连接相关的配置
●http块:代理、缓存、日志记录、虚拟主机配置
■http全局块
■Server块
◆Server全局块
◆location块
注意: http块中可以配置多个Server块,每个Server块中可以配置多个location块。
nginx具体应用
部署静态资源
概述:Nginx可以作为静态web服务器来部署静态资源。静态资源指在服务端真实存在并且能够直接展示的一些文件,比如常见的html页面、css文件、js文件、图片、视频等资源。
优势:相对于Tomcat, Nginx处理静 态资源的能力更加高效,所以在生产环境下,一般都会将静态资源部署到Nginx中。
将静态资源部署到Nginx非常简单,只需要将文件复制到Nginx安装目录下的html目录中即可。
server {
listen 80; #监听端口
server.name localhost; 服务器名称(一般是域名)
location/ { #匹配客户端请求url
root html; #指定静态资源根目录
index index.html; #指定默认首页
}
}
正向代理概念:
●正向代理
是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求井将获得的内容返回给客户端。
正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。
正向代理一般是在客户端设置代理服务器,通过代理服务器转发请求,最终访问到目标服务器。
反向代理概念:
●反向代理
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源,反向代理服务器负责将请求转发给目标服务器。
用户不需要知道目标服务器的地址,也无须在用户端作任何设定。
如:客户端—->反向代理服务器(192.168.217.128)—->web服务器(192.168.217.129)
●配置反向代理
这个机器ip为192.168.217.128
server {
listen 82;
server_name localhost;
location / {
proxy_pass http://192.168.217.129:8080; #反向代理配置,将请求转发到指定服务
}
}
这个意思就是和上面举的例子一样,就是把对这台服务器82端口的请求,代理到http://192.168.217.129:8080中
负载均衡(在反向代理基础上,本质就是反向代理)
背景:早期的网站流量和业务功能都比较简单,单台服务器就可以满足基本需求,但是随着互联网的发展,业务流量越来越大井且业务逻辑也越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器组成应用集群,进行性能的水平扩展以及避免单点故障出现。
●应用集群:将同一应用部署到多台机器上,组成应用集群,接收负载均衡器分发的请求,进行业务处理并返回响应数据
●负载均衡器:将用户请求根据对应的负载均衡算法分发到应用集群中的一台服务器进行处理
配置负载均衡:
upstream targetserver{ #upstream指令可以定义一组服务器
server 192.168.217.129:8080;
# 可以在后面添加weight
# server 192.168.217.129:8080 weight=10;
server 192.168.217.130:8080;
}
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://targetserver;
}
}
负载均衡策略:
轮询:就是代理端依次去访问每一个服务器(默认)
权重轮询:就是给服务器加上权重,代理端会根据权重分配访问量
ip_hash:他对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。
least_conn:一句最少连接方式
url_hash:一句url分配方式
fair:依据响应时间方式
注意:在这里iphash可以解决session不共享问题,但是不建议使用他,建议使用redis
前后端分离开发
介绍:
前后端分离开发,就是在项目开发过程中,对于前端代码的开发由专门的前端开发人员负责,后端代码则由后端开发人员负责,这样可以做到分工明确、各司其职,提高开发效率,前后端代码并行开发,可以加快项目开发进度。
目前,前后端分离开发方式已经被越来越多的公司所采用,成为当前项目开发的主流开发方式。
前后端分离开发后,从工程结构上也会发生变化,即前后端代码不再混合在同一个maven工程中,而是分为前端工程和后端工程。
前端工程打包部署在nginx中,后端代码打包部署到tomcat
开发流程:
定制接口(定义规范)--->前端开发(mock数据测试)、后端开发(后端测试)---->连调(校验格式)---->测试
注意:接口(API接口)就是一个http的请求地址,主要就是去定义:请求路径、请求方式、请求参数、响应数据等内容。mock数据就是前端自己模拟测试。
前端技术栈
开发工具
●Visual Studio Code
●hbuilder
技术框架
●nodejs(类似于后端中jdk的作用)
●VUE
●ElementUl(组件库)
●mock(生成模拟数据)
●webpack(进行前端打包的)
后端技术栈
YApi
介绍
YApi是高效、易用、功能强大的api管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护API, YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
YApi让接口开发更简单高效,让接口的管理更具可读性、可维护性,让团队协作更合理。
类似于Apifox,postman等
源码地址: https://github.com/YMFE/yapi
要使用YApi,需要自己进行部署。
Swagger
介绍
使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。
官网: https://swagger.io/
knife4j是为JavaMVC框架集成Swagger生成Api文档的增强解决方案。
使用方式
操作步骤:
1、导入knife4j的maven坐标
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2.导入knife4j相关配置类
3、设置静态资源映射(WebMvcConfig类中的addResourceHandlers方法) ,否则接口文档页面无法访问
添加到方法里即可;
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/")
4、在LoginCheckFilter中 设置不需要处理的请求路径
常用注解
@Apl:用在请求的类上,例如Controller, 表示对类的说明
@ApiModel:用在类上,通常是实体类,表示一个返回响应数据的信息
@ApiModelProperty:用在属性上,描述响应类的属性
@ApiOperation:用在请求的方法上,说明方法的用途、作用
@ApilmplicitParams:用在请求的方法上,表示一组参数说明
@ApilmplicitParam:用在@ApilmplicitParams注解中,指定一个请求参数的各个方面
项目部署
部署架构
使用192.168.217.128进行nginx部署前端项目,192.168.217.129进行springboot内嵌的tomcat部署后端项目,使用192.168.217.128进行主数据库存储和redis缓存,192.168.217.129进行slave数据库存储
部署环境说明
服务器:
●192.168.217.128 (服务器A)
Nginx:部署前端项目、配置反向代理
Mysql:主从复制结构中的主库
●192.168.217.129 (服务器B)
jdk:运行Java项目
git:版本控制工具
maven:项目构建工具
jar: SpringBoot项目打成jar包基于内置Tomcat运行
Mysql:主从复制结构中的从库
●172.17.2.94 (服务器C,在这里我们用服务器A代替)
Redis:缓存中间件
部署前端项目
①在服务器A安装nginx,将dish目录上传到nginx的html目录下(dish就是前端项目打完包后的文件)
②配置nginx.conf
#瑞吉外卖反向代理配置
location / {
root html/dist;
index index.html;
}
location ^~ /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://192.168.217.129:8080;
}
注意:rewrite表示url重写,这一段其实就是重写url把api后面的截取也就是如:/employee/login
部署后端项目
①在服务器b安装jdk,git,maven,mysql,使用git clone命令将git远程仓库的代码克隆下拉
②在/usr/local/reggie进行创建reggieStart.sh启动脚本
③在此文件根目录执行./reggieStart.sh启动脚本进行部署
测试:
在浏览器输入192.168.217.128进行登录即可
RuiJiWaiMai-0.0.1-SNAPSHOT.jar