苍穹外卖笔记----2024.07.31

项目概述,环境搭建

(一)软件开发整体介绍

软件开发流程

需求分析:说明书,产品原型

设计:UI设计,数据库设计,接口设计

编码:项目代码,单元测试

测试:测试用例,测试报告

上线运维:软件环境安装,配置

角色分工

软件环境

(二)苍穹外卖项目介绍

约定

dto用于接收,用于前端传给后端

vo用于返回,用于回显数据

前后端分离,Nginx负责前端(如登录页面),idea的Tomcat服务器启动负责后端(如是否能登录成功)

项目介绍
定位

功能架构

产品原型
技术选型

(三)开发环境搭建

整体结构
前端环境搭建

主要注重后端开发,前端代码已打包,通过nginx运行起来即可完成搭建

后端环境搭建
        熟悉项目结构

sky-common

constant:常量类

context:上下文

enumeration:枚举类

exception:自定义异常类

json:处理json转换的类

properties:springboot中的配置属性类

result:后端返回结果

utils:工具类

sky-pojo

DTO:例如前端提交了一个json数据,对应的后端需要进行接收该数据并封装成java对象,这个java对象就由DTO来接收。也就是说,前端和后端要传输数据,最终要封装成一个对象,此时就可以使用DTO

sky-server

        使用Git进行版本控制

具体操作:

1.创建Git本地仓库

提交到本地仓库

2.创建Git远程仓库

sky-take-out

3.将本地文件推送到Git远程仓库

将以下文件加入到.gitignore目录,表示不用git来管理

target:编译后产生的目录,里面放着一些class文件

idea:idea自带的文件

iml:idea自带的文件

class:编译后的类

Test.java:测试类

test:测试包

        数据库环境搭建

1.通过数据库建表语句创建数据库表结构

2.查看数据库设计文档熟悉数据库表结构

        前后端联调

在运行之前先进行编译,保证编译通过然后才能运行

登陆成功

思考:前端发送的请求,是如何请求到后端的?

解答:Nginx反向代理,将前端发送的动态请求由nginx转发到后端服务器

比如登录请求,并不是由前端直接请求到后端tomcat服务器的,而是由nginx做了转发

Nginx反向代理的好处,即为什么不直接请求tomcat服务器

1.提高访问速度。在请求nginx时,在nginx这一层可以做缓存,如果请求的是同样的接口地址,就无需请求后端服务,nginx这一层直接将缓存数据返回给前端。

2.负载均衡。将大量的请求按照我们指定的方式均衡分配给集群中的每台服务器。例如,当某个系统上线之后,由于访问压力比较大,后端需要部署多台服务器来构成一个集群,此时nginx可以作为一个负载均衡器,前端发送的大量请求可以通过Nginx负载均衡器均匀的分配给后端的各个服务器。如果没有Nginx负载均衡器,前端需要直接去访问后端的服务器并且只能访问固定的某一台服务器,有了nginx之后,则可以将请求分配给多台服务器。

3.保证后端服务的安全。一般情况,后端服务器在公司企业的内网不能暴露出来,只能通过nginx反向代理请求后端服务。

Nginx反向代理和负载均衡的配置

完善登录功能

加密处理

导入接口文档

(四)Swagger

参数如果过多,不便于使用postman,因此选择Swagger

步骤:

输入localhost:8080/doc.html会出现接口文档,是由Knife4J动态生成的

并且一一对应

这些文档内容是通过解析出来得到的

通过Swagger进行接口测试

该Swagger接口文档不仅可以查看接口,还可以进行接口测试

注意:
1.如果没有加静态资源映射会出现404报错

原因:当我们请求doc.html时,SpringMvc框架认为我们请求的不是一个静态资源(因为没有),而是一个controller,而此时我们并没有对应的controller

2.如果扫包的位置有误,接口文档无法获取我们想要的结果

Swagger注解

描述当前这个类的作用

描述这个方法的作用

描述实体类(专注于数据传输的实体)的作用

添加注解的效果展示

这些参数都是通过注解解析得来的

员工管理,分类管理

(一)新增员工

需求分析
产品原型:

思考限制条件,业务规则

接口设计:

例如添加员工,思考请求方式->post请求方式;思考提交数据的格式->json格式;思考后端返回的数据格式->一般情况也是json格式。并且该项目遵循约定:后端响应给前端的数据整体会封装成Result对象来返回。

数据库设计:

代码开发
根据新增员工接口设计对应的DTO:

设计DTO用于封装前端提交过来的数据。

注意:当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据

整体思路:

通过DTO封装数据,传给service,在service中把没有涉及到的属性进行封装,再调用mapper执行insert语句插入数据库

封装数据

调用持久层进行sql插入

        

功能测试

接口文档测试

测试前需要进行登录,获取jwt令牌,再进行全局参数设置传入jwt令牌

前后端联调测试

代码完善

处理Sql异常



动态获取用户登录id

思考(新技术点ThreadLocal):
解析出登陆员工id后,如何传递给Service的save方法?

使用条件

客户端每一次发出的请求,tomcat都会为其分配一个单独的线程,在这个线程会执行不同的代码功能,比如拦截器,controller,service等等,它们都属于同一个线程,满足这个条件,就可以使用ThreadLocal,然后将数据存进去,在对应的地方取出来,所以通过ThreadLocal这个存储空间,即可达到相应的效果

验证:每一次请求都会对应一个单独的线程

点击保存执行一次

再次点击保存,重新发起请求即重新获取一个单独的线程

ThreadLocal常用方法

注意:

在我们使用ThreadLocal时,往往对其进行封装成一个工具类,便于使用

实现思路

在已经解析出用户id的地方,进行调用ThreadLocal.set方法,将id存进去 

然后在service中进行取出

(二)员工分页查询

代码完善

如图可知日期代码格式存在问题

解决方法

方法一

格式正常

方法二

消息转换器

格式正确

(三)启用禁用员工账号

需求分析和设计

接口设计

代码开发

思考:

这里我们将update作为一个通用性的修改更新操作,不局限于当前的修改员工账号状态

因此,传入一个实体类更合适----->要想传入实体类,需要先创建一个对象----->引申出builder创建对象的方法

编写动态sql,将可能修改到的字段一一列举,以后修改类的操作都可以调用这条sql

功能测试

(四)编辑员工

需求分析和设计

代码开发

controller

serviceImpl

EmployeeMapper/EmployeeService

功能测试

(五)导入分类模块功能代码

需求分析和设计

代码导入

导入代码顺序mapper->service->controller不报错

功能测试

测试通过

菜品管理

(一)公共字段自动填充

问题分析

实现思路

代码开发

创建一个包,用于存放注解

自定义注解AutoFill/使用枚举

 自定义切面类AutoFillAspect
创建包/类

切入点

通知

在Mapper方法上加入AutoFill注解

CategoryMapper也要加

之前对公共字段进行赋值的操作可以省去

功能测试

测试完成

(二)新增菜品

需求分析和设计
产品原型和规则

接口设计

数据库设计(dish菜品表和dish_flavor口味表)

代码开发
开发文件上传接口
接收文件

将文件上传至阿里云服务器

编写配置文件

注意:

采用主配置文件引用具体配置文件的方法,然后在具体配置文件中进行配置

借助工具类AliOssUtil

需要进行文件上传,就要调用工具类,工具类要想将文件传到阿里云,就需要对以下四个属性进行赋值

AliOssProperties对象

从配置类读取的文件封装成一个properties对象,并且加了@component注解,可以直接作为参数进行注入(OssConfiguration配置类),从而将对象创建出来,这个配置类在项目启动时又会自动加载

具体文件上传代码

测试通过

“前端会请求我们刚刚设置的路径,从而进行显示图片”是因为

  • 前端HTML中的<img>标签指定了图片资源的路径。

  • 浏览器在解析HTML时,会识别这些路径自动向服务器发起请求

  • 服务器根据请求的路径返回相应的图片资源

  • 浏览器接收并渲染这些图片资源,最终在用户界面上显示它们

开发新增菜品接口

封装DTO数据

调用业务层/声明方法

操作数据库

菜品表

口味表

功能测试

(三)菜品分页查询

问题分析

代码开发

根据菜品分页查询接口定义设计对应的DTO

根据菜品分页查询接口定义设计对应的VO

categoryName在菜品表中不存在,但是页面却需要展示分类的名称categoryName,因此需要创建VO并且扩展了categoryName属性,将VO转成json格式数据给到前端,前端就能正常展示

功能测试

(四)删除菜品

问题分析

代码开发
疑惑解答

为什么这段代码可以直接通过获得id来获得实体类dish? Dish dish = dishMapper.getById(id);

解答:这段代码之所以能够直接通过 id 获得 Dish 实体类实例,是因为它背后有 ORM 框架(这里是指mybatis)的支持,该框架自动处理了 SQL 语句的执行(sql语句还是需要自己编写)、结果的查询以及 Java 对象与数据库表之间的映射等复杂操作。

功能测试

(五)修改菜品

问题分析

代码开发
根据id查询菜品信息,用于数据回显

修改菜品
功能测试

测试通过

套餐管理

(一)新增套餐

需求分析和设计

Setmeal相关的实体类

Setmeal                                                          SetmealVO

SetmealDTO

代码实现
功能测试(略)

(二)套餐分页查询

需求分析和设计
代码实现
功能测试(略)

(三)删除套餐

需求分析和设计
代码实现
功能测试(略)

(四)修改套餐

需求分析和设计
代码实现
功能测试(略)

(五)起售停售套餐

需求分析和设计
代码实现
功能测试(略)

店铺营业状态设置

需求分析和接口设计

代码开发

该部分代码采用管理端+用户端区分的代码实现,并且区分了swagger的客户端和用户端

管理端admin

设置店铺状态

获取店铺状态

用户端

swagger分组
功能测试

测试通过

微信登陆,商品浏览

HttpClient

介绍

在java中通过编码的方式发送http请求

入门案例

aliyunOss底层已经包含了httpClient,因此不需要额外导入坐标

代码部分:

get方式请求

测试通过,注意,先运行启动项再运行测试项,并且需要将redis跑起来,如果还是报500错误,需要去页面将营业状态status修改一下即可

后续使用httpclient可以直接使用该工具类

微信小程序开发

介绍

准备工作

获取小程序ID和密钥

勾选

入门案例

目录结构

view标签类似于div标签

微信登录

导入小程序代码

微信登录流程

需求分析

微信登录流程: 获取用户openid(唯一标识符)---->通过code(临时授权码)获得

小程序端向后端发送请求,并提交授权码code,通过授权码code去调用微信接口服务,给用户返回令牌,令牌包含用户的唯一标识        

代码开发
配置微信登录所需配置项

配置为微信用户生成jwt令牌时使用的配置项

微信登录部分

需要传入密钥,超时时间,map对象。前面两个已经配置在配置文件中

获取配置文件中的值:

配置文件(.yml)中的配置项(图2)会封装到对应的配置属性类(图3 JwtProperties)中,并且该类加上了component注解,即可直接在UserController注入JwtProperties对象,注入对象之后就可以直接调用方法获取值

由于最终返回的是vo,vo中不仅有token还有id,openid,所以需要对其进行封装

封装vo

以上完成了接口的定义,接下来要进行接口的实现

功能测试

导入商品浏览功能代码

需求分析

四个接口

查询分类的接口

查询当前选中的分类下的菜品,根据某个菜品分类id,查询对应的菜品的接口

查询套餐的接口,根据分类id查套餐

根据套餐id,查询包含哪些菜品的接口

注意

口味数据不需要单独接口去查,当我们查询某个菜品时,即可关联的查询其对应的口味数据

查询分类接口

type如果不传参数,则表示查所有分类

根据分类id查询菜品接口 

flavors当前菜品包含的口味数据,口味有多个,因此使用数组,其下又有各类属性故为object类型

根据分类id查询套餐接口

根据套餐id查询包含的菜品

缓存商品,购物车

(一)缓存菜品

问题说明

实现思路

Redis:基于内存保存数据,本质上是对内存的操作

数据库:对磁盘IO的操作

大体思路:先查缓存,如果有,读取缓存直接使用,没有则查数据库,然后将查到的数据放入缓存

缓存逻辑分析

1.每个分类下的菜品保存一份缓存数据,即按分类缓存,有多少个分类则有多少份缓存数据,Redis是键值对的形式,则一份缓存数据对应一对key-value

key-分类Id categoryId

value-分类下具体的菜品数据,这些菜品使用string字符串(这里的string字符串是redis的数据类型)保存。java中的数据类型和redis的数据类型不是一一匹配的,java的任何对象都可以转成redis的string数据类型进行存储

从java的角度看,分类下的菜品数据为一个list集合,可以将list集合序列化,转成redis的字符串来存储2.数据库中菜品数据有变更时清理缓存数据

补充:序列化和反序列化

代码开发
1.每个分类下的菜品保存一份缓存数据
2.数据库中菜品数据有变更时清理缓存数据

功能测试

(二)缓存套餐

(三)添加购物车

需求分析

故意设计冗余字段可以提高查询速度,但不能设置数量过多并且只能用于不经常改变的字段

代码开发

根据接口文档可知,添加操作只需要返回状态码即可,因此可以不需要指定泛型

添加购物车需要考虑到的条件

1.判断当前加入到购物车的商品是否存在

2.如果已经存在,则数量+1

3.如果不存在,则需要插入一条数据-->插入的数据可以是菜品,也可以是套餐,因此需要进行判断

功能测试

(四)查看购物车

需求分析和设计

代码开发

查看购物车包括下面的清空购物车,不需要传递参数,只需查看所有/清空所有购物车数据

功能测试

(五)清空购物车

需求分析和设计

代码开发

易,略

功能测试

(六)从购物车减去商品 【作业】

实现类

初次尝试的错误思考:

删除数据的时候应该先确保购物车有要删除的商品,再考虑商品数量为1和不为1的情况,而不是数量为0和大于0的情况

正确写法

还需要利用一个根据id删除的方法

用户下单,订单支付

(一)导入地址簿功能代码

需求分析和设计

代码开发
功能测试

(二)用户下单

需求分析和设计

接口分析

用户下单-->本质上是新增操作insert-->将下单的数据插入到数据库

需要提交的参数:提交地址簿id-->通过地址簿可以查到用户的各类信息

代码开发
1.处理各种业务异常(地址簿为空,购物车数据为空--->注入对应的mapper)

2.向订单表插入1条数据(不管有几个商品,都是一条数据)

插入数据之后需要返回主键值,订单明细实体类会使用到当前订单的id,因此需要返回主键值

useGeneratedKeys表示返回主键值,id对应订单实体类的id属性

3.向订单明细表插入n条数据(订单表和订单明细表是一对多的关系)

4.清空当前用户的购物车数据

调用之前的方法

5.封装vo返回结果

功能测试

(三)订单支付

微信支付介绍

重要的两个接口

提供给商户使用,商户先调用该接口在微信支付服务后台生成预支付交易单,该接口会返回一个数据,叫做预支付交易会话标识(一个字符串),拿到这个数据之后再按照前端不同的支付场景生成交易串,前端拿到交易串就可以调起微信支付

后端将数据计算好返回给小程序,小程序再调用该方法wx.requestPayment--->弹出支付窗口(调起微信支付)--->用户输入支付密码完成真正付款

微信支付准备工作

思考:
(1)调用过程如何保证数据安全?
(2)微信后台如何调用到商户系统?

调用过程本质也是http请求,但是当前商户系统的ip地址若是当前笔记本的ip地址(局域网内的ip地址),微信后台无法调用到,除非当前笔记本获取到一个公网ip,获取方式:内网穿透--->获得一个临时的ip,这个ip是一个公网ip,此时微信后台可以请求到商户系统。

代码导入

功能测试

作业-->用户端历史订单模块

(一)查询历史订单

需求分析和设计
代码开发

功能测试

(二)查询订单详情

需求分析和设计

代码开发

BeanUtils.copyProperties通常用于复制具有相同属性名称的对象的属性。如果OrderVO没有与orderDetailList中对象相对应的集合属性或者相应的设置逻辑来处理这种复制,那么这种方法可能是错误的。

BeanUtils.copyProperties复制ordersorderVO,然后显式地设置orderDetailList。这种方法更加明确,并且通常是处理此类封装情况的首选方法,因为它确保了订单详情列表被正确地设置到OrderVO对象中。

功能测试

(三)取消订单

需求分析和设计

代码开发

注意:

此处重新new一个Orders对象而不是直接使用上面的ordersDB进行封装是因为:

通过创建一个新的Orders对象并设置其ID,更清晰地表明它正在准备一个用于更新的订单对象,而不是直接修改从数据库中检索出来的对象。这种做法有助于保持原始数据库实体的不变性,防止数据库数据流出和潜在的副作用。

功能测试

(四)再来一单

需求分析和设计

代码开发

分析:

  1. orderDetailList.stream():将订单详情列表转换为一个Stream,以便您可以在其上进行一系列的操作。

  2. .map(x -> {...}):使用map操作将Stream中的每个元素(在这里是x,代表单个订单详情转换为一个新的对象。在这个例子中,您将每个订单详情转换为一个ShoppingCart对象。

  3. 在map操作的lambda表达式内部

    a. 创建一个新的ShoppingCart对象。

    b. 使用BeanUtils.copyProperties(x, shoppingCart, "id");将订单详情对象x的属性复制到新创建的shoppingCart对象中,除了"id"属性(这意味着您不想复制ID)。

    c. 设置shoppingCartuserId为某个特定的用户ID(这个用户ID应该是在方法外部定义的,或者是作为参数传递给这个方法的)。

    d. 设置shoppingCartcreateTime为当前时间。

  4. .collect(Collectors.toList());将Stream中的元素收集到一个新的List中。在这个例子中,您将得到一个新的List<ShoppingCart>,其中包含根据订单详情列表转换而来的购物车对象

功能测试

作业-->商家端订单管理模块

(一)订单搜索

需求分析和设计

代码开发
功能测试

(二)各个状态的订单数量统计

需求分析和设计
代码开发
功能测试

(三)查询订单详情

需求分析和设计
代码开发
功能测试

(四)接单

需求分析和设计
代码开发
功能测试

(五)拒单

需求分析和设计
代码开发
功能测试

(六)取消订单

需求分析和设计
代码开发
功能测试

(七)派送订单

需求分析和设计
代码开发
功能测试

(八)完成订单

需求分析和设计
代码开发
功能测试

订单状态定时处理,来电提醒和客户催单

Spring Task

cron

订单状态定时处理

WebSocket

介绍

入门案例

注重流程,代码并非重点

来电提醒

客户催单

完结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值