苍穹外卖
苍穹外卖
〇 经验总结
0 自动装配
- 主启动类上的注解
@SpringbootApplication
是一个复合注解,其中比较重要的@SpringbootConfiguration
:springboot的相关配置@EnableAutoConfiguration
@ComponentScan
:扫描一些包并注入
- 重点
@EnableAutoConfiguration
,也是一个复合注解@import(AutoConfigurationImportSelect.class)
AutoConfigurationImportSelect
中一个重要的方法:importSelect()
作用是选取一些类,注入到IOC容器中配置文件的信息如何加载到bean中:spring.factories文件存储了一些键信息,然后加载的时候配置文件的值就被加载到这个文件中了
两种
@Autowired
:根据类型自动注入@Resource
:根据bean的名称自动注入
1 Mybatis
分页查询
PageHelper
:原理是存储分页信息在ThreadLocal
中,在执行sql前拦截器会读取到分页信息动态插入到sql中再执行
批量插入
mapper:
参数传递
xxxMapper.java
中方法的形参名可以不与.xml
一致,MyBatis 会按照参数的位置进行匹配而不是参数的名字,若有多个参数,则应该按顺序使用- 也可以使用
@Param
:List<TeachplanDto> selectTreeNodes(@Param("courseId") Long courseId, @Param("name") String name);
指定mapper中的名字
- 也可以使用
2 微信登录
过程
前端传入授权码发送至服务端
服务端携带授权码code + appid + secret 采用
HttpClient
发送请求到https://api.weixin.qq.com/sns/jscode2session得到微信服务端返回的openid + session_key,其中openid也就是微信用户的唯一标识
根据openid查询数据库中是否存在此用户(openid不是主键),若不存在则自动创建,存在则返回该用户信息
为该用户生成jwt令牌(密钥+过期时间+信息)
- 密钥用于加密解密jwt信息
- 信息claims是一个map,在本项目中只存储了userId这一个信息
返回VO对象
<img src=(https://myl-mdimg.oss-cn-beijing.aliyuncs.com/TyporaImg/苍穹外卖.assets/image-20240224190705160.png“ alt=”image-20240224190705160” style=”zoom: 67%;” />
3 redis和SpringCache
redis缓存菜品分类
根据分类id查询菜品存储在List中,随后redis的key为dish_id号,value是list的内容
当新增、修改、删除菜品的时候删除所有的dish_*的缓存(这里可以做的更细一点)
<img src=(https://myl-mdimg.oss-cn-beijing.aliyuncs.com/TyporaImg/苍穹外卖.assets/image-20240224195415885.png“ alt=”image-20240224195415885” style=”zoom:67%;” />
SpringCache缓存套餐
本质:SpringCache在JVM中,集群中不可用
application
类上添加@EnableCaching
在根据分类id查询套餐前判断setmealCache这个缓存管理器中有没有指定的分类id的value,有则直接返回,没有则再执行方法,再将查询结果存储在缓存中
<img src=(https://myl-mdimg.oss-cn-beijing.aliyuncs.com/TyporaImg/苍穹外卖.assets/image-20240224201425719.png“ alt=”image-20240224201425719” style=”zoom:67%;” />
在批量删除套餐的时候清理所有缓存
<img src=(https://myl-mdimg.oss-cn-beijing.aliyuncs.com/TyporaImg/苍穹外卖.assets/image-20240224201343039.png“ alt=”image-20240224201343039” style=”zoom:67%;” />
4 微信支付
- 用户点击支付,向接口发送请求,传入订单号及付款方式(微信)
- 服务端获取到当前用户的id,采用JSAPI下单,传入appid、mchid(商户号)、description、out_trade_no、notify_url(回调地址)等信息,发送post请求(httpclient)https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
- 解析返回结果:prepay_id(关键,这个要给前端): 预支付交易会话标识,根据prepay_id及appid、时间等信息生成二次签名(预支付交易单)
- 支付结果异步通知:读取数据、解密数据、业务处理:修改订单状态、给微信响应
5 百度地图
- 申请ak
- 请求的时候注意参数的顺序:address、output、ak
- 获取经纬度信息:https://api.map.baidu.com/geocoding/v3
- 获取路径规划信息:https://api.map.baidu.com/directionlite/v1/driving
6 来单提醒/用户催单
客户端通过
/ws/{sid}
与服务端建立websocket长连接,会话对象Session
存储在map中,当需要发送消息的时候调用session.getBasicRemote().sendText(message);
方法即可在客户支付成功之后调用
Ⅰ 整体介绍
一 软件开发整体介绍
1.1 流程
- 需求分析:需求规格说明书、产品原型
- 设计:UI设计、数据库设计、接口设计
- 编码:项目代码、单元测试
- 测试:测试用例、测试报告
- 上线运维:软件环境安装、配置
1.2 角色分工
1.3 软件环境
- 开发环境:开发阶段环境,一般外部用户无法访问
- 测试环境:测试人员使用,一般外部用户无法访问
- 生产环境:线上环境,正式提供对外服务的环境
二 苍穹外卖项目介绍
2.1 项目介绍
定位:专门为餐饮企业(餐厅、饭店)定制的一款软件产品
功能架构
2.2 产品原型
- 产品原型:用于展示项目的业务功能,一般由产品经理进行设计
2.3 技术选型
技术选型:展示项目中使用到的技术框架和中间件等
三 开发环境搭建
- 整体结构
- 前端
- 管理端:Web
- 用户端:小程序
- 后端
- 后端服务:Java
- 前端
3.1 前端环境搭建
- 前端工程基于nginx运行
- 双击项目目录下的nginx.exe即可启动服务,端口号为80
3.2 后端环境搭建
后端工程基于maven进行项目构建,并且进行分模块开发
sky-take-out:maven父工程,统一管理依赖版本,聚合其他子模块
- sky-common:子模块,存放公共类,例如:工具类、常量类、异常类等
- sky-pojo:子模块,存放实体类、VO、DTO等
- entity:实体,通常与数据库的表对应
- DTO:数据传输对象,通常于程序中各层之间传递数据
- VO:视图对象,为前端展示数据提供的对象
- POJO:普通Java对象,只有属性和对应的getter和setter
- sky-server:子模块,后端服务,存放配置文件、Controller、Service、Mapper等
使用Git进行版本控制
数据库环境搭建
前后端联调
- 前端请求地址:http://localhost/api/employee/login
- 后端接口地址:http://localhost:8080/admin/employee/login
- nginx 反向代理,就是将前端发送的动态请求由 nginx 转发到后端服务器
- 提高访问速度
- 进行负载均衡
- 保证后端服务安全
3.3 完善登录功能
- 问题:员工表中的密码是明文存储,安全性低
- 思路:将密码加密后存储,提高安全性,使用MD5加密方式对明文密码加密
四 接入接口文档
4.1 前后端分离开发流程
- 在YApi中管理接口
五 Swagger
- 类似于postman 但是postman一个一个测试比较麻烦
5.1 介绍
使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。
5.2 使用方式
- 导入坐标
- 配置类中加入配置
- 设置静态资源映射,否则接口文档页面无法访问
5.3 常用注解
在com.sky.config.WebMvcConfiguration.java中定义
启动后端项目后:http://localhost:8080/doc.html 可访问生成的文档
Ⅱ 员工管理、分类管理
一 新增员工
1.1 需求分析和设计
产品原型
- 账号唯一
- 手机号合法的
- 身份证合法
- 密码默认123456
接口设计
请求参数
- Path:/admin/employee
- Method:POST
返回数据
约定
- 管理端的请求,/admin作为前缀
- 用户端的请求,/user作为前缀
数据库设计(employee表)
1.2 代码开发
根据新增员工接口设计对应的DTO
注意:当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据
1
2
3
4
5
6
7
8
9
public class EmployeeDTO implements Serializable {
private Long id;
private String username;
private String name;
private String phone;
private String sex;
private String idNumber;
}
1.3 功能测试
通过接口文档测试(多用这种)
通过前后端联调测试
由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成,
导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。
注意配置token,在接口文档中定义一个全局变量,key为token(有时效的)
1.3 代码完善
录入的用户名已存在,抛出异常后没有处理
全局异常处理器处理
新增员工时,创建人id和修改人id设置为了固定值
通过某种方式动态获取当前登录员工的id
- 员工登录成功后会生成JWT令牌并响应给前端
- 后续请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录员工id
- 解析出登录员工id后,如何传递给Service的save方法?
ThreadLocal
- 并不是一个Thread,而是Thread的局部变量。
- ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
- 客户端发送的每次请求,后端的Tomcat服务器都会分配一个单独的线程来处理请求
- 在拦截器中解析出当前登录的员工的id,并放入的线程局部变量中,随后在保存的时候读取这个id
二 员工分页查询
2.1 需求分析和设计
业务规则
- 根据页码展示员工信息
- 每页10条数据
- 分页查询时可以根据需要,输入员工姓名进行查询
接口设计
请求参数
- Path:/admin/employee/page
- Method:GET
返回数据
2.2 代码开发
根据接口设计对应的DTO
后面所有的分页查询,统一都封装成PageResult对象:
员工信息分页查询后返回的对象类型为:
Result<PageResult>
2.3 功能测试
2.4 代码完善
方式一:在属性上加入注解,对日期进行格式化
方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
三 启用禁用员工账号
3.1 需求分析与设计
业务规则
- 可以对状态为“启用” 的员工账号进行“禁用”操作
- 可以对状态为“禁用”的员工账号进行“启用”操作
- 状态为“禁用”的员工账号不能登录系统
接口设计
3.2 代码开发
3.3 功能测试
四 编辑员工
4.1 需求分析与设计
业务规则
- 根据id查询员工信息
- 编辑员工信息
接口设计
根据id查询员工信息
编辑员工信息
4.2 代码开发
4.3 功能测试
五 导入分类模块功能代码
- 与员工管理类似
5.1 需求分析与设计
业务规则
- 分类名称必须是唯一的
- 分类按照类型可以分为菜品分类和套餐分类
- 新添加的分类状态默认为“禁用”
接口设计
- 新增分类
- 分类分页查询
- 根据id删除分类
- 修改分类
- 启用禁用分类
- 根据类型查询分类
数据库设计(category表)
5.2 代码开发
5.3 功能测试
Ⅲ 菜品管理
一 公共字段自动填充
1.1 问题分析
业务表中的公共字段重复,导致代码冗余、不便于后期维护
1.2 实现思路
- 自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
- 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
- 在 Mapper 的方法上加入 AutoFill 注解
- 技术点:枚举、注解、AOP、反射
1.3 代码开发
- 自定义注解AutoFill
- 自定义切面 AutoFillAspect
- 完善自定义切面 AutoFillAspect 的 autoFill 方法
- 在Mapper接口的方法上加入 AutoFill 注解
- 将业务层为公共字段赋值的代码注释掉
1.4 功能测试
二 新增菜品
2.1 需求分析和设计
业务规则
- 菜品名称必须是唯一的
- 菜品必须属于某个分类下,不能单独存在
- 新增菜品时可以根据情况选择菜品的口味
- 每个菜品必须对应一张图片
接口设计
根据类型查询分类(已完成)
文件上传
新增菜品
数据库设计(dish菜品表和dish_flavor口味表)
2.2 代码开发
2.3 功能测试
三 菜品分页查询
3.1 需求分析和设计
业务规则
- 根据页码展示菜品信息
- 每页展示10条数据
- 分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
接口设计
3.2 代码开发
3.3 功能测试
四 删除菜品
4.1 需求分析和设计
业务规则
- 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除掉
接口设计
数据库设计
4.2 代码开发
4.3 功能测试
五 修改菜品
5.1 需求分析和设计
接口设计
根据id查询菜品
根据类型查询分类(已实现)
文件上传(已实现)
修改菜品
5.2 代码开发
5.3 功能测试
Ⅳ 店铺状态
一 Redis
1.1 简介
- Redis是一个基于内存的 key-value 结构数据库
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
- 企 业应用广泛
- 网站
- 下载与安装
1.2 Redis服务启动与停止
服务端
- 服务启动命令cmd:
redis-server.exe redis.windows.conf
- Redis服务默认端口号为 6379 ,通过快捷键Ctrl + C 即可停止Redis服务
- 服务启动命令cmd:
客户端
客户端连接命令cmd:
redis-cli.exe
通过redis-cli.exe命令默认连接的是本地的redis服务,并且使用默认6379端口。也可以通过指定如下参数连接:
-h ip地址
-p 端口号
-a 密码(如果需要)
设置Redis服务密码
修改redis.windows.conf
requirepass 123456
客户端图形工具
- Another-Redis-Desktop-
二 Redis数据类型
2.1 5种常用数据类型
- 字符串
String
- 普通字符串,Redis中最简单的数据类型
- 哈希
hash
- 也叫散列,类似于Java中的HashMap结构
- 列表
list
- 按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
- 集合
set
- 无序集合,没有重复元素,类似于Java中的HashSet
- 有序集合
sorted set/zset
- 集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
三 Redis常用命令
字符串操作命令
SET key value
GET key
SETEX key seconds value
:设置指定key的值,并将 key 的过期时间设为 seconds 秒SETNX key value
:只有在 key 不存在时设置 key 的值
哈希操作命令
HSET key field value
:将哈希表 key 中的字段 field 的值设为 valueHGET key field
:获取存储在哈希表中指定字段的值HDEL key field
:删除存储在哈希表中的指定字段HKEYS key
:获取哈希表中所有字段HVALS key
:获取哈希表中所有值
列表操作命令
LPUSH key value1 [value2]
:将一个或多个值插入到列表头部(左边)LRANGE key start stop
:获取列表指定范围内的元素RPOP key
:移除并获取列表最后一个元素(右边)LLEN key
:获取列表长度
集合操作命令
SADD key member1 [member2]
:向集合添加一个或多个成员SMEMBERS key
:返回集合中的所有成员SCARD key
:获取集合的成员数SINTER key1 [key2]
:返回给定所有集合的交集SUNION key1 [key2]
:返回所有给定集合的并集SREM key member1 [member2]
:删除集合中一个或多个成员
有序集合操作命令
ZADD key score1 member1 [score2 member2]
:向有序集合添加一个或多个成员ZRANGE key start stop [WITHSCORES]
:通过索引区间返回有序集合中指定区间内的成员ZINCRBY key increment member
:有序集合中对指定成员的分数加上增量 incrementZREM key member [member ...]
:移除有序集合中的一个或多个成员
通用命令
KEYS pattern
:查找所有符合给定模式( pattern)的 keyEXISTS key
:检查给定 key 是否存在TYPE key
:返回 key 所储存的值的类型DEL key
:该命令用于在 key 存在是删除 key
四 java中操作Redis
Redis的java客户端很多,常用的几种
Jedis
Lettuce
Spring Data Redis :是 Spring 的一部分,对 Redis 底层开发包进行了高度封装。
在 Spring 项目中,可以使用Spring Data Redis来简化操作。
步骤
导入Spring Data Redis坐标
配置数据源
编写配置类,创建Redis Template对象
通过Redis Template对象操作Redis
RedisTemplate
:针对大量api进行了归类封装,将同一数据类型的操作封装为对应的Operation接口,具体分类如下:- ValueOperations:string数据操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:hash类型的数据操作
- ListOperations:list类型的数据操作
五 店铺营业状态设置
5.1 需求分析和设置
接口设计
设置营业状态
管理端查询营业状态
用户端查询营业状态
数据存储方式:基于Redis的字符串来进行存储
- 约定:1表示营业。0表示打烊
5.2 代码开发
5.3 功能测试
Ⅴ 微信登录 商品浏览
一 HttpClient
1.1 介绍
HttpClient是Apache的一个子项目,是高效的、功能丰富的支持HTTP协议的客户端编程工具包。
作用
- 发送HTTP请求
- 接收响应数据
核心API
- HttpClient
- HttpClients
- CloseableHttpClient
- HttpGet
- HttpPost
发送请求步骤
- 创建HttpClient对象
- 创建Http请求对象
- 调用HttpClient的execute方法发送请求
1.2 入门案例
二 微信小程序开发
2.1 介绍
2.2 准备工作
- 创建完善信息
- 获取APPID以及密钥
- ID:wx51bff83287f6cf1e
- 密钥:8d1d0cba4123d1b03f772b50f0266abc
2.3 入门案例
了解小程序的目录结构
小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
一个小程序页面由四个文件组成:
编写小程序代码
编译小程序
三 微信登录
3.1 微信登录流程
3.2 需求分析和设计
业务规则
基于微信登录实现小程序的登录功能
如果是新用户需要自动完成注册
接口设计
数据表设计(user表)
3.3 代码开发
- 配置微信登录所需配置项:
- 配置为微信用户生成jwt令牌时使用的配置项:
- DTO设计:
- VO设计:
- Controller、service、Mapper的编写
3.4 功能测试
四 导入商品浏览功能代码
4.1 需求分析和设计
查询分类
根据分类id查询菜品
根据分类id查询套餐
根据套餐id查询包含的菜品
Ⅵ 缓存商品、购物车
一 缓存菜品
1.1 问题说明
用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。
1.2 实现思路
通过Redis来缓存菜品数据,减少数据库查询操作。
缓存逻辑分析
- 每个分类下的菜品保存一份缓存数据
- 数据库中的菜品数据有变更时清理缓存数据
1.3 代码开发
- 修改管理端接口 DishController 的相关方法,加入清理缓存的逻辑,需要改造的方法:
- 新增菜品
- 修改菜品
- 批量删除菜品
- 起售、停售菜品
1.4 功能测试
二 缓存套餐
2.1 Spring Cache
Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能
Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:
- EHCache
- Caffeine
- Redis
常用注解
2.2 实现思路
- 导入坐标
- 在启动类上加入@EnableCaching注解,开启缓存注解功能
- 在用户端接口SetmealController的 list 方法上加入@Cacheable注解
- 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解
2.3 代码开发与功能测试
三 添加购物车
3.1 需求分析和设计
接口设计
数据库设计
作用:暂时存放所选商品的地方
- 选的什么商品,每个商品买了几个,不同的用户的购物车需要分开
四 查看购物车
4.1 需求分析和设计
接口设计
五 清空购物车
需求分析和设计
Ⅶ 用户下单 订单支付
一 导入地址簿功能
1.1 查询地址列表
查询登录用户所有地址
查询默认地址
1.2 新增地址
接口
1.3 修改地址
1.4 删除地址
根据id删除地址
1.5 设置默认地址
1.6 查询默认地址
根据id查询地址
1.7 数据表(address_book)
二 用户下单
2.1 需求分析和设计
业务说明
- 电商系统中,用户是通过下单的方式通知商家,用户已经购买了商品,需要商家进行备货和发货。
- 用户下单后会产生订单相关数据,订单数据需要能够体现如下信息:
- 总金额
- 用户
- 商品种类及数量
- 收获地址
- 手机号码
业务流程
- 购物车页面
- 订单提交页面
- 订单支付页面
- 下单成功页面
接口分析
数据库设计
订单表
orders
订单明细表
order_detail
订单表和订单明细表的关系:一对多
2.2 代码与测试
三 订单支付
3.1 微信支付介绍
微信支付产品
微信支付接入流程
微信小程序支付时序图
微信小程序调起支付:通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付
3.2 微信支付准备工作
- 如何保证数据安全
- 微信后台如何调用到商户系统?
- 获取微信支付平台证书、商户私钥文件
- 获取临时域名:支付成功后微信服务通过该域名回调我们的程序
3.3 代码与功能测试
- (看看就好)
Ⅷ 订单处理
一 Spring Task
1.1 介绍
Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
作用:定时自动执行某段Java代码
- 应用场景
- 信用卡每月还款提醒
- 银行贷款每月还款提醒
- 火车票售票系统处理未支付订单
- 入职纪念日为用户发送通知
1.2 cron表达式
cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间
构成规则
分为6或7个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)
- cron表达式在线生成器:https://cron.qqe2.com/
1.3 demo
导入坐标
spring-context
启动类添加注解
@EnableScheduling
开启任务调度自定义定时任务类(要加入
@Component
交给容器管理)
二 订单状态定时处理
2.1 需求分析
场景
下单后未支付,订单一直处于“待支付”状态
用户收货后管理端未点击完成按钮,订单一直处于“派送中”状态
逻辑
- 通过定时任务每分钟检查一次是否存在支付超时订单(下单后超过15分钟仍未支付则判定为支付超时订单),如果存在则修改订单状态为“已取消”
- 通过定时任务每天凌晨1点检查一次是否存在“派送中”的订单,如果存在则修改订单状态为“已完成”
2.2 代码与功能测试
- 自定义定时任务类OrderTask
- 在OrderMapper接口中扩展方法
三 WebSocket
3.1 介绍
WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
与http对比
应用场景
- 视频弹幕
- 网页聊天
- 体育实况更新
- 股票基金报价实时更新
缺点
- 服务器长期维护长连接需要一定的成本
- 各个浏览器支持程度不一
- WebSocket 是长连接,受网络限制比较大,需要处理好重连
- 结论:WebSocket并不能完全取代HTTP,它只适合在特定的场景下使用
3.2 demo
- 直接使用websocket.html页面作为WebSocket客户端
- 导入WebSocket的maven坐标
- 导入WebSocket服务端组件WebSocketServer,用于和客户端通信
- 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件
- 导入定时任务类WebSocketTask,定时向客户端推送数据
四 来电提醒
4.1 需求分析与设计
需求分析
- 语音播报
- 弹出提示框
设计
- 通过WebSocket实现管理端页面和服务端保持长连接状态
- 当客户支付后,调用WebSocket的相关API实现服务端向客户端推送消息
- 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报
- 约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,content
- type 为消息类型,1为来单提醒 2为客户催单
- orderId 为订单id
- content 为消息内容
五 客户催单
- 需求分析
- 语音播报
- 弹出提示框
- 设计
- 通过WebSocket实现管理端页面和服务端保持长连接状态
- 当客户支付后,调用WebSocket的相关API实现服务端向客户端推送消息
- 客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报
- 约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,content
- type 为消息类型,1为来单提醒 2为客户催单
- orderId 为订单id
- content 为消息内容
Ⅸ 数据统计-图形
一 Apache ECharts
1.1 介绍
- Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
总结:使用Echarts,重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表。
二 营业额统计
2.1 需求分析和设计
业务规则
- 营业额指订单状态为已完成的订单金额合计
- 基于可视化报表的折线图展示营业额数据,X轴为日期,Y轴为营业额
- 根据时间选择区间,展示每天的营业额数据
接口设计
三 用户统计
3.1 需求分析和设计
业务规则
- 基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数
- 根据时间选择区间,展示每天的用户总量和新增用户量数据
接口设计
四 订单统计
4.1 订单统计
业务规则
- 有效订单指状态为 “已完成” 的订单
- 基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量
- 根据时间选择区间,展示每天的订单总数和有效订单数
- 展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率 = 有效订单数 / 总订单数 * 100%
接口
五 销量排名
业务规则
- 根据时间选择区间,展示销量前10的商品(包括菜品和套餐)
- 基于可视化报表的柱状图降序展示商品销量
- 此处的销量为商品销售的份数
接口
Ⅹ 数据统计-excel
一 工作台
展示数据
- 今日数据
- 订单管理
- 菜品总览
- 套餐总览
- 订单信息
接口设计
今日数据接口
订单管理接口
菜品总览接口
套餐总览接口
订单搜索
各个状态订单数量统计
二 Apache POI
2.1 介绍
- Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。
- 一般情况下,POI 都是用于操作 Excel 文件。
2.2 应用场景
- 银行网银系统导出交易明细
- 各种业务系统导出Excel报表
- 批量导入业务数据
2.3 demo
- 导入坐标
- 读写
三 导出运营数据Excel报表
业务规则
导出Excel形式的报表文件
导出最近30天的运营数据
接口设计
设计excel模板文件
- 查询数据
- 将数据写入到模板文件
- 通过输出流将Excel文件下载到客户端浏览器
十一 苍穹外卖部署管理端到远程服务器
参考https://blog.csdn.net/weixin_74266825/article/details/134899613
及https://zhuanlan.zhihu.com/p/660284743
- 预期结果:实现本地和远程的一键启动,无需额外启动mysql redis nginx
目录结构
- sql生成脚本存储在mysql-init目录下,便于创建相关数据表及导入数据
sky-server工程
sky-server下新建
Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 使用包含 Java 8 的官方 OpenJDK 基础镜像
FROM openjdk:8-jdk-alpine
# 创建一个目录来存储你的应用
WORKDIR /app
# 将你的 Spring Boot 应用的 jar 文件复制到镜像中
# 假设你的 jar 文件名为 sky-server-1.0-SNAPSHOT.jar,并且位于 target 目录下
COPY ./target/sky-server-1.0-SNAPSHOT.jar /app/sky-server-1.0-SNAPSHOT.jar
# 向外界暴露 8080 端口
EXPOSE 8080
# 定义容器启动时运行的命令
# 使用 java -jar 命令启动 Spring Boot 应用
CMD ["java", "-jar", "/app/sky-server-1.0-SNAPSHOT.jar"]新建一个
application-prod.yml
,主要是修改了数据库和redis的host和port1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32# 远程服务器版
sky:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
host: sky-take-out-mysql # 通过容器的id来访问对应的容器
port: 3306 # 应用和mysql容器是在同一个网络中,所以直接访问3306端口,如果是在不同网络中,需要访问外部映射端口,也就是3305
database: sky_take_out
username: xx
password: xx
#阿里oss文件上传的配置
alioss:
endpoint: xx
access-key-id: xx
access-key-secret: xx
bucket-name: xx
redis:
host: sky-take-out-redis
port: 6379
#password:
database: 0
wechat:
secret: xxx
appid: xxx
shop:
address: xxx
baidu:
ak: xxx之后
application.yml
工作环境也修改为prod1
2
3
4
5
6
7
8
9
10
11server:
port: 8080
spring:
config:
use-legacy-processing: true
profiles:
active: prod
...
docker compose文件
分为service和env两个,其中service主要是mysql、redis等依赖服务,env是项目服务
在项目目录下新建
docker-compose-service.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44version: '3'
services:
mysql:
image: mysql:8 # 使用的镜像
container_name: sky-take-out-mysql # 启动的实例名称,这里的name和刚刚在-prod.yml中指定的host一致
environment:
MYSQL_ROOT_PASSWORD: 1234 # root 用户密码
# 可以在这里添加额外的环境变量, 但更改认证插件需要使用 command 选项 客户端连接mysql8.0有点问题,设置了这个
command: --default-authentication-plugin=mysql_native_password
ports:
- "3305:3306" # 端口映射
volumes:
- ./.mysql-data:/var/lib/mysql # 将数据目录挂载到本地目录以进行持久化,这个目录存储的就是数据,可以自动生成
- ./mysql-init:/docker-entrypoint-initdb.d # 自动执行启动脚本,这个mysql-init是需要手动创建的,目录里面存放sql脚本用来初始化数据库
restart: always # 崩溃后自动重启
networks:
- mynetwork # 指定网络
redis:
image: redis:6
container_name: sky-take-out-redis
ports:
- "6380:6379"
networks:
- mynetwork
volumes:
- ./.redis-data:/data # 持久化,目录可以自动生成
# nginx后面讲
nginx:
image: nginx:latest
container_name: sky-take-out-nginx
ports:
- "8081:80" # 将宿主机的 8081 端口映射到容器的 80 端口,项目访问入口:ip:8081
volumes:
- ./nginx/html:/usr/share/nginx/html:ro # 将宿主机的 /data/html 映射到容器的 /usr/share/nginx/html 以提供静态内容
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf # 使用宿主机的 Nginx 配置文件替换容器中的默认配置
networks:
- mynetwork
networks:
mynetwork: # 自定义网络,实现网络互通和隔离在项目目录下新建
docker-compose-env.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14version: '3.8'
services:
sky-take-out-service:
container_name: sky-take-out-service # 这里的name需要记住,后面nginx有用
build: ./sky-server # 会去 sky-server 目录下寻找Dockerfile文件
ports:
- "8080:8080" # 将容器的 8080 端口映射到宿主机的 8080 端口
volumes:
- ./sky-server:/app/data # Optional: 如果你需要持久化日志等数据,或者直接运行编译后的代码
networks:
- mynetwork
networks:
mynetwork: # 定义一个网络,使得未来的服务能加入到同一网络中
nginx
项目目录下的nginx文件夹
这里的html和资料中的nginx里面的一样,conf也是,接下来对conf进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42...
upstream webservers{
server sky-take-out-service:8080 weight=90 ; # 直接指定容器名称和容器内部的端口,这里的sky-take-out-service就是docker-compose-env.yml中配置的,端口也是
}
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html/sky; # 这里的目录需要修改,这个目录是在docker-compose-service.yml中nginx部分配置的
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# 反向代理,处理管理端发送的请求
location /api/ {
proxy_pass http://webservers/admin/;
}
# 反向代理,处理用户端发送的请求
location /user/ {
proxy_pass http://webservers/user/;
}
# WebSocket
location /ws/ {
proxy_pass http://webservers/ws/;
proxy_http_version 1.1;
proxy_read_timeout 3600s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "$connection_upgrade";
}
}
...
本地测试
执行
mvn clean
,mvn package -DskipTests
在target目录下生成jar包在
docker-compose-service.yml
中点击启动(这里nginx会启动失败,先不管)工具测试是否可以连接,mysql端口是3305 redis是6380
再运行
docker-compose-env.yml
,项目成功启动过一分钟会有超时订单的处理也可以判断是否成功连接数据库
回去再把nginx容器启动了
- 访问localhost:8081
- 以后就可以直接这样本地启动了
部署到云服务器
准备一个云服务器 centos 7,安装好maven、docker
在根目录下创建
code/sky-take-out
目录先在本地执行
mvn clean
删除jar包,删掉刚刚生成的文件夹,只需要图中右边的那些,然后上传整个项目目录到服务器的目录中进入到
sky-take-out
目录执行
mvn package -DskipTests
打包启动环境依赖服务,也可以不用sudo
sudo docker compose -f docker-compose-service.yml up -d
启动sky-take-out服务,也可以不用sudo
sudo docker compose -f docker-compose-env.yml up -d
在docker restart一下nginx容器
- 最后访问 服务器ip:8081即可