苍穹外卖开发课程笔记(烂尾版)

DAY1(项目概述,环境搭建)

软件开发整体介绍

软件开发流程

1.需求分析

需求规格说明书,产品原型

2.设计

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

3.编码

项目代码,单元测试

4.测试

测试用例,测试报告

5.上线运维

软件环境安装,配置

角色分工

软件环境

1.开发环境develpoment:开发人员在开发阶段使用的环境,外部用户无法访问

2.测试环境testing:测试服务器,部署到并给测试人员测试

3.生产环境production:线上环境,正式提供对外服务的环境

苍穹外卖项目介绍

项目介绍

定位:为餐饮企业定制的软件产品,管理端和用户端

项目架构:(体现项目中的业务功能模块)

产品原型

用于展示项目的业务功能,一般由产品经理进行设计,一般是html页面

技术选型

展示项目中使用到的技术框架和中间件

开发环境搭建

前端环境搭建

前端工程基于nginx实现,启动nginx服务可以访问,默认80端口。

后端环境搭建

后端工程基于maven进行项目构建,分模块开发.

git版本控制

通过IDEA的VCS创建本地仓库,共享到github。

数据库环境搭建

通过Navicat运行sql语句,创建数据库,表结构。

前后端联调

第一个遇到的问题就是默认sql数据库密码和自己的数据库密码不一样,在配置文件中更改,解决。

通过在controller的login方法处打断点进行单步执行,来debug观察流程,了解异常类的定义和捕获.

生成JWT令牌(不懂,去学)

利用builder来构建传回前端页面的返回数据VO

直接给了后端工程,要去了解一下登录功能从0到1的过程

ngnix反向代理

将前端发送的动态请求由nginx转发到后端服务器

HTTP和反向代理web服务器

好处和优点:

1提高访问速度

2进行负载均衡(把大量的请求按照我们制定的方式均衡分配给集群中的每台服务器)

3保证后端服务的安全

nginx反向代理的配置方式:

负载均衡配置方式:

负载均衡基于反向代理实现,都是proxy_pass

负载均衡策略:

完善登录功能

员工表密码明文存储,安全性低.

用md5加密后存储,明文加密MD5只能加密,是单向的。

// TODO可以标记遗留标签页,可以方便后期查看

//md5传入的是byte数组
password = DigestUtils.md5DigestAsHex(password.getBytes());

数据库改成md5加密后字符串,serviceimpl中使用工具md5加密

导入接口文档

前后端分离开发流程

用Yapi进行接口文档导入,Yapi pro接口管理平台。

导入json类型的接口文件。

Swagger

介绍

使用swagger需要按照它的规范去定义接口以及接口相关信息,就可以租到生成接口文档,以及在线接口调试页面。

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

使用方式

1.导入knife4j的maven坐标

2.在配置类中加入knife4j相关配置

3.设置静态资源映射,否则接口文档页面无法访问

Yapi和Swagger: 1Yapi是设计阶段使用的工具,管理和维护接口

2.Swagger是后端开发阶段使用的框架,帮助开发人员做接口测试

常用注解

通过注解可以控制生成的接口文档,使接口文档具有更好的可读性,常用注解如下:

DAY2(员工管理,分类管理)

新增员工

需求分析和设计

账户的唯一性,手机号为合法的11位手机号码,身份证号校验18位,密码默认为123456

代码开发

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

1.通过DTO封装数据(Controller层)

2.在Controller层中传给employeeService.

3.在Service的实现ServiceImpl中采用属性拷贝,并且把没有涉及到的属性封装

4.在ServiceImpl中调用Mapper执行Insert语句

5.返回Result对象

功能测试

1.通过接口文档测试(常用)

jwt令牌校验(传过来的token与服务器token进行比较)

在swagger中登录获取jwt令牌token,设置swagger全局变量进行测试

2.通过前后端联调测试(需要前端功能编写完毕)

代码完善

1.录入用户名存在,抛出异常未处理

全局异常处理器捕获sql相关异常,添加捕获,判断异常信息里是否包含关键语句,返回Result结果并提示msg错误信息。

2.新增员工时创建人修改人id设置为了固定值

jwt的大致流程:

动态方式获取id,在拦截器取得token中保存的id.

ThreadLocal并不是一个Thread,而是Thread的局部变量。

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

注:每个线程中执行一系列的代码操作.

可以将ThreadLocal的值设置为当前用户的id,在请求发到服务端时候设置,在新增用户设置创建人修改人时候get取到,实现动态获取。

员工分页查询

需求分析和设计

前端提交的数据,后端发送给前端的结果数据

不是json格式而是Query地址栏问号的形式传参

这一步骤主要是熟练接口

代码开发

1.根据前端传来的数据设计DTO

2.统一封装成PageResult对象再用Result的泛型设置为PageResult返回给前端

(注解问题GetMapping和PostMapping值的是访问url,RequestMapping是最大的一级)

3.代码编写,从Controller到Service到ServiceImpl到Mapper

4.这里是动态sql,需要编写xml文件

5.借助PageHelper来简化分页(底层基于ThreadLocal来实现的,存入变量page,在sql语句中插入,来实现limit)

功能测试

问题描述:准备测试打开doc.html,也就是knife4j框架的文档页面,发现接口全部消失。

解决:通过排查knife4j配置文件以及项目结构发现是因为不知道什么时候把controller包放进来handler包,导致扫描不到接口。(我真蠢QAQ)

文档接口测试要注意token的有效期,在application配置的。

前后端联调测试发现创建时间不符合阅读习惯,计划修改。

代码完善

时间问题

1.属性上加注解,对日期进行格式化(只能进行单个属性的格式化)

2.在WebMvcConfiguration中扩展Spring MVC的消息转化器,统一对日期类型进行格式化处理。 (可以统一处理,进行一系列格式转换)

一系列的操作》消息转换器》对象转换器(直接给出)

启用禁用员工账户

需求分析和设计

可以对状态为启动的进行禁用操作

可以对状态为禁用的员工账号进行启用操作

状态为禁用的员工账号不能登录系统

还是熟悉接口,能够知道应该用什么接受,传出什么,用什么注解

代码开发

注意:如果是查询类,需要返回数据,所以要加上泛型,但是非查询类可以不用加泛型

路径参数要加上注解@PathVariable

json格式要加上@RequestBody

常规业务代码开发感悟:

1.在Controller中新建对应方法,写入对应参数,注意参数的注解{例如上面},和整个方法的注解(PostMapping?GetMapping?ApiOpreation?)

2.首先在Controller拿到前端传来的数据,一般都用单独对应的DTO进行封装(好像是MVC框架的功能特性)

3.根据功能进行数据的返回,用的是统一的Result封装结果

4.调用Service的方法(在Service接口中新建)

5.对应的在ServiceImpl中实现接口

6.在实现中执行具体的逻辑代码操作,不同的要求有不同的逻辑,目的是实现功能,通常要调用对应的Mapping操作数据层(sql数据库)

7.在对应的Mapping中新建方法并实现sql语句(其中根据功能的要求有多种实现方法,包括但不限于a直接用@Insert()注解写简单的sql,b在对应的mapping.xml中写sql语句,c有的功能还需要借助例如PageHelper等工具来实现动态sql的编写,d直接用等标签实现sql的动态编写)

8.记得完善注释,没实现的地方要加//TODO标记,get请求要注意返回的类型(很重要)

功能测试

两种测试方法均无误

编辑员工

需求分析和设计

根据id查询员工,用于点修改按钮后在新页面回显信息

编辑员工信息修改类是put的请求方式

代码开发

根据接口进行代码开发

常规开发,注意要分两步走,先是数据回显,然后是数据修改,要点已经总结。

功能测试

问题:编辑员工功能测试返回500报错

数据库异常。

解决:经过排查后发现是Mapper中的的update里的sql语句update_User忘记加_了,导致程序找不到这个属性。

已解决

导入分类模块功能代码

需求分析和设计

分类的名称唯一

分类需要按照类型分:菜品分类和套餐分类

新添加的分类默认为禁用状态

接口设计分析,数据库设计分析

代码导入

从底向上导入,可以不报错,compile一下防止不自动编译

功能测试

测试不解的地方:为什么删除分类时候要考虑是否关联菜品?

DAY3(菜品管理)

公共字段自动填充(策略)

问题分析

业务表中的公共字段

JAVA代码存在冗余,不便于维护

通过切面AOP来统一拦截

1.自定义注解AutoFill,用于标识需要进行公共字段填充的方法

2.自定义切面类AutoFillAspect,拦截加入了AutoFill注解的方法,通过反射为公共字段赋值

技术点:枚举,注解,AOP,反射

实现思路

代码开发

@Target注解自JDK1.5之后就有了,其作用是定义在注解的上方,表明其注解的作用范围。

@Retention修饰注解,用来表示注解的生命周期。

@Aspect 就是把一个类定义为切面供容器读取。

@@Component:定义Spring管理Bean(也就是将标注@Component注解的类交由spring管理)

@Slf4j是用作日志输出的,一般会在项目每个类的开头加入该注解,如果不写下面这段代码,并且想用log,就必须写代码:

private final Logger logger = LoggerFactory.getLogger(当前类名.class);

切入点,即能通过@PointCut中的模式字符串匹配到的方法。

//拦截Mapper包下所有的类* 所以的方法* 匹配所有参数类型(..),并且只对加了AutoFill注解的方法起作用
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")

AOP中有关键步骤有三个:切面,切入点,通知(代码增强部分,有很多类型)

1.获取到当前拦截的方法在数据库上操作的类型

2.获取到操作的对应实体对象(参数)

3.准备赋值的数据(时间和当前登录用户的id)

4.根据不同的操作类型,对实体对象进行赋值

功能测试

测试无误,但是要加强java基础的学习,不懂的地方主要是反射,注解这一类的问题和对应的处理过程,抽空去复习吧.....

新增菜品

需求分析和设计

要求:

菜品名称必须唯一

菜品必须属于分类下,不能单独存在

新增菜品时可以根据情况选择菜品的口味

菜品必须对应一张图片

接口设计:

根据类型查询分类

文件上传

新增菜品

代码开发

文件上传接口开发:

@RestController 是@controller和@ResponseBody 的结合

@Controller 将当前修饰的类注入SpringBoot IOC容器,使得从该类所在的项目跑起来的过程中,这个类就被实例化。 @ResponseBody 它的作用简短截说就是指该类中所有的API接口返回的数据,甭管你对应的方法返回Map或是其他Object,它会以Json字符串的形式返回给客户端

阿里云对象存储OSS

解决阿里云对象存储在java中的配置(包括配置文件,Oss工具类,将工具类实例化,导入)

回到commonController继续代码编写,upload方法uuid防止文件重命名

图片上传完成。

@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。在使用@Autowired之前,我们对一个bean配置起属性时,是这用用的

    

通过这种方式来,配置比较繁琐,而且代码比较多。

@Transactional 能保证方法内多个数据库操作要么同时成功、要么同时失败。

使用时要记得在启动类打开注解方式的事务管理

处理菜品sql时要处理sql语句xml配置,返回主键的值赋给id,在Service里可以get获得id,为接下来的口味准备数据。

                  返回生成主键              返回到id这个值上

        insert into dish (name,category_id,price,image,description,create_time,update_time,create_user,updte_user,status)
            value
        (#{name},#{categoryId},#{price},#,#{image},#{description},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
    

还有Lambda表达式的内容。

功能测试

在阿里云OSS记得把权限改为公共读,不然无法调用程序读上传的照片进行回显。

第一个问题:检测传入的DishMapper为空。

解决:排查后发现未加入@Autowired注解,所以在Spring中找不到

第二个问题:Parameter index out of range (4 > number of parameters, which is 3).

解决:发现是sql语句中,多打了一个 ,# 导致在传入第四个值时找不到对应的第四个占位符。

第三个问题:Unknown column 'updte_user' in 'field list'

解决:依然是sql语句编写问题,改为 update_user,打错字了

菜品分页查询

需求分析和设计

一般类查询操作,接口,传入传出,涉及多张表

代码开发

1设计DTO接受前端传来的数据

2还要设计对应的VO(因为有categoryId这个数据)

sql的编写还是重点

功能测试

问题:测试发现查询为空,什么都没有?

解决:排查发现DishController里面分页查询方法的返回值为默认的null,当然啥都查不出来

删除菜品

需求分析和设计

接口设计:

传入只需要一个包含id的数组

代码开发

传入的id数组是json格式,可以用字符串接受,自己去拆解,也可以借助MVC帮助我们直接得到一个数组

动态sql的编写和将sql写入java都是对我来说的难点

功能测试

错误:sql语句忘记加右括号

修改菜品

需求分析和设计

有回显需求

接口设计:

1根据id查询菜品

2根据类型查询分类1

3文件上传1

4修改菜品

代码开发

1根据id查询菜品

重点是DishVo是返回对象,除了用BeanUtils复制dish属性以外还要手动设置设置dish里没有的flavor属性

2.修改菜品

sql中对于可能导致多种结结果的编写很难想到(口味先全部删除,再修改)

功能测试

一次成功

感谢:业务逻辑中的增删改查并不是有手就行,sql的编写,如何让程序更高效,如何符合传入传出的数据要求,解决问题的思路和逻辑,都很重要

DAY4(自行编写套餐实战接口)

新增套餐

分析产品原型和表结构--编写时候要从全局考虑复用,尽可能高内聚。

代码编写,自己慢慢写

发现问题,新建套餐时候增加菜品按照名字搜索功能未完善。

测试出现问题:无法自动装配/未找到“XXX“类型的Bean

解决:百度发现是自己ServiceImpl上没有加@Service注解,第一次实践,不知道@Service注解加在ServiceImpl上.....

问题:小问题不断,几乎全是sql语句的编写错误,这是我的不足之处

测试完成,新增套餐接口实现成功。

套餐分页查询

编写完毕,重点是PageHelper的使用和传入值返回值的提前设置。

删除套餐

@RequestParam:把请求中的指定名称的参数传递给控制器中的形参赋值

测试成功一部分,单个停售商品删除成功

全写完再测试//TODO

修改套餐

谔谔,资料有一部分没有,只能靠自己照着之前的修改来写了

错误:sql编写出错,在set语句后没加, 这是基础知识不扎实的问题,sql真的很重要

自行编写测试通过~

起售停售套餐

路径参数和地址栏传参数:

路径参数就是@PathVariable那个,地址栏传参是Query,可以不用@RequestParam,但是要保证传参和controller中接受的参数名字一样

DAY5 (Redis,营业状态设置)

Redis入门

基于!内存存储!的key-value结构的数据库

mysql是磁盘存储(介质不同,存储结构不同-sql是表结构)

1Redis基于内存存储,读写性能高,但是内存有限

2存储热点数据(热点商品,新闻,资讯)-访问量大的时间段或者服务

复习了一下redis命令行操作

(图形界面链接需要服务端开启)

Redis数据类型

各种数据类型的特点

Redis常用命令

Mysql中对不同数据类型,类似插入这种命令相同,都是insert,但是Redis则不一样,不同数据类型,插入命令不同。

字符串操作命令

哈希操作命令

列表操作命令

集合操作命令

有序集合操作命令

通用集合操作命令

在JAVA中操作Redis

Redis的Java客户端

SDR是对前两者的高度封装

Spring Data Redis使用方式

测试编写:

需要@Autowried自动注入引入的类

注意用@SpringVootTest注解引入上下文

@SpringBootTest:注解可以用来标记一个测试类,它告诉Spring Boot启动一个完整的应用程序上下文,而不仅仅是一个单一的测试类或测试方法。这个完整的应用程序上下文将包含所有的Spring Bean、配置和依赖项,这样我们就可以像在实际的应用程序中一样运行我们的测试用例。

在JAVA中操作redis数据库

问题:nested exception is io.lettuce.core.RedisConnectionException: Unable to conn

Client sent AUTH, but no password is set

redis的图形化界面连接时配置了密码,如果服务端没有设置密码,可以直接连接数据库,如果服务端设置了密码,假如配置密码正确,可以验证后进入。

但是Java中配置类,如果服务端设置密码,java配置文件没写密码,报错,

如果服务端没设置密码,java配置类写了密码,也报错连接错误,错误提示不需身份验证。

店铺营业状态设置

需求分析和设计

接口设计

为了一个数据做一张表不太值得

所以用基于redis的字符串存储

代码开发

用户端和管理端路径请求不同所以分开写Controller,在两个软件包中。

注入Spring里,类名不能一样,会报冲突

Annotation-specified bean name 'shopController' for bean class [com.sky.controller.user.ShopController] conflicts with existing, non-compatible bean definition of same name and class [com.sky.controller.admin.ShopController]

解决:在Controller注解@RestController里面指定注入bean的名字

功能测试

DAY6(用户登录,浏览菜品)

HttpClient

在JAVA程序中通过编码的方式来发送Http请求。

入门案例

    public void testGET() throws Exception {
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建Http请求对象
        HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
        //调用方法excute发送请求
        CloseableHttpResponse response = httpClient.execute(httpGet);
​
        //获取返回的状态码
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println("状态码:"+statusCode);
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity);
        System.out.println("服务端返回数据:"+body);
        //关闭资源
        response.close();
        httpClient.close();
    }
​
    /**
     * POST方式请求
     */
    @Test
    public void testPOST() throws Exception {
        CloseableHttpClient aDefault = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
        //构造数据
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username","admin");
        jsonObject.put("password","123456");
        //json格式转String
        StringEntity entity = new StringEntity(jsonObject.toString());
        //设置字符编码
        entity.setContentEncoding("utf-8");
        //设置类型
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        //发送请求
        CloseableHttpResponse response = aDefault.execute(httpPost);
        //解析数据
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println("状态码:"+statusCode);
        HttpEntity entity1 = response.getEntity();
        String body = EntityUtils.toString(entity1);
        System.out.println("响应数据:"+body);
        //关闭数据
        response.close();
        aDefault.close();
    }

在开发中可以直接调用封装好的工具类

微信小程序开发

介绍

准备工作

入门案例

小程序目录结构

(前端开发)

微信登录

导入代码

导入后记得修改配置.

微信登录流程

小程序文档

Postman接口功能测试

需求分析设计

基于微信登录实现小程序的登录功能

如果是新用户需要自动完成注册

接口设计分析

代码开发

其他步骤类似,不过需要加入为用户生成jwt令牌来通过后端的拦截器.

后端还要再写一个拦截器专门用来处理用户端发送的请求,需要把拦截器注入到配置里面

注:这里的前后端联调中的前端是微信小程序端,其他和之前类似。

关键是要按照微信小程序的规定与约束进行登录功能,得到数据后存储到sql数据库,再进行相关操作。

功能测试

前后端联调~

新奇的体验

导入商品浏览功能代码

需求分析

增删改查

接口设计:

业务规范:

这是业务必不可少的地方

代码导入

按部就班

功能测试

困扰了半小时的问题:

小程序前端请求后status请求并没有被拦截器忽略,而是正常验证token,但此时并没有登录所以没有token,报错token1为空;

解决:排除摸索看评论后发现问题出在注册拦截器的环节:

registry.addInterceptor(jwtTokenUserInterceptor)
     .addPathPatterns("/user/**")
     .excludePathPatterns("/user/user/login")
     .excludePathPatterns("/user/shop/status");

后两个user前面没写/!!!!!

比较遗憾的是并不是自己耐心排查出来的......心性还需要磨练,发现问题,仔细排查,解决问题,希望接下来的工作生活中能沉得住气。

DAY7(缓存商品,购物车)

缓存菜品

问题说明

访问量过大,数据库的访问压力随之增大,查询性能下降-----系统响应慢,用户体验差,很多时候都是出在数据库这一端

解决:通过Reidis来缓存菜品数据,减少数据库查询操作

java和redis中的数据类型并不完全一样,java中的任何数据类型可以转化为字符串存储在redis的value的位置。

1.每个分类下的菜品保存一份缓存数据

2.数据库中菜品数据有变更时及时清理缓存数据

缓存与数据库一致性解决方案:1.延时双删2.异步更新缓存

代码开发

为什么写在Controller层不是Service层?

因为service可能涉及到多个表一起操作,这个类似于在最外层过滤操作一次,层次不一样。

功能测试

发现修改菜品后缓存并不清楚

解决:排查问题代码发现是传入的模式"dish_"忘记加通配符*

缓存套餐

Spring Cache

@Cacheable涉及到一个代理对象,由代理对象先进行缓存查询

@CacheEvict伴随着sql删除而删除

实现思路

代码开发

在对应的Controller方法加上注解即可。

功能测试

测试无误

添加购物车

需求分析设计

老三样,接口设计,传入数据选择,返回数据分析。

代码开发

购物车中的商品存在,执行update更新数量。

功能测试

无误

查看购物车

完成

清空购物车

稀松平常的CRUD

DAY8(用户下单,订单支付)

导入地址簿功能代码

不同业务不同要求,不同代码编写,整体上不难,需要思考,或者经验。

用户下单

下单后订单通知商家进行备货。

接口设计,数据库设计,很重要

重复工作就不写了,纯纯CRUD码农,没前途,会了

订单支付

看看就行了,面试根本涉及不到?完全是微信支付的文档内容。

内网穿透工具可能得看看

DAY9

大致看了一遍,除了百度地图的调用比较新颖,其他还是增删改查,业务复杂度不一,非目前首要目标。

DAY10(SpringTask,WebSocket新技术)

Spring Task

介绍

Spring框架提供任务调度工具,可以按照约定的时间自动执行某个代码逻辑。

作用:定时自动执行某段Java代码。

场景:

信用卡还款提醒(短信)

银行贷款每月还款提醒

火车票售票系统处理未支付订单

入职纪念日发送通知

一切需要判断条件,定时执行的程序。。。。

cron表达式

本质上是字符串,通过cron表达式可以定义任务触发的时间

WebSocket

介绍

Http网络协议和WebSocket协议

视频弹幕,网页聊天,体育实况更新,股票基金报价实时更新

入门案例

服务端最重要的是WebSocketService.

DAY11(Apache Echarts,Apache POI)

前端技术Apache Echarts.

JS实现表格

数据查询是大头,SQL绝对的重中之重。

常用API可以直接看文档使用。

完结撒花~

你可能感兴趣的:(java,spring,sql,后端)