第二章 苍穹外卖

发布于:2025-05-19 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

constant:存储的是定义好的常量类

context:存储与上下文相关的

enumeration:存储枚举类

exception:存储一些异常

json:处理一些json转换的类

properties:存储一些配置类

result:存储一些后端的返回结果

utils:存储一些工具类

开发环境搭建_后端环境搭建_使用Git进行版本控制

首先,用gitignore忽略掉不需要上传的文件内容:

在本地创建本地仓库:

在菜单栏选择创建你本地git仓库,选定根目录的地址。再将本地代码提交给本地仓库。

创建远程仓库:

复制仓库地址url,并创建新的远程,建立新的远程分支

直接选择推送,代码就推送成功了。

后端环境搭建_数据库环境搭建

有专门的数据库建表文档可以查看

后端环境搭建_前后端联调

在这里主要是启动了nginx,前后端联调测试了以下员工登录功能是否成功

(这一段代码我将解析的十分详细,因为使用了很多的常量类枚举类自定义异常,我需要快速熟悉并适应这样成熟化的书写格式)

/**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录:{}", employeeLoginDTO);

        Employee employee = employeeService.login(employeeLoginDTO);

代码解析:定义了一个login方法用于实现登录功能,使用@Requestbody注解将前端传输到的json转换为对应的可接收的dto对象。

在以前我们没有具体明确Result类返回的到底是什么类型的返回值,现在有了各种VO的明确建立,我们则需要在Result的泛型当中具体明确我们所需要的具体的VO对象

然后创建了一个employee对象用于接收employeeService调用login方法得到的结果

login方法的具体实现如下:

   /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // TODO 后期需要进行md5加密,然后再进行比对
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

}

先是创建了两个string类型的对象用于存储获取到的用户名和密码,然后用获取到的用户名调用employeeMapper中的getByUsername方法获取到具体数据,并用employee将数据保存。接下来进行的是数据判断:

如果用户不存在,抛出一个我们定义好的异常类AccountFoundException,被使用super关键字传给这个异常类的父类的msg内容是我们定义好的常量类中的一个常量 ACCOUNT_NOT_FOUND

这个常量的具体内容如下:

public static final String ACCOUNT_NOT_FOUND = "账号不存在";

然后判断密码是否存在,该账号是否被锁定,如果一切ok,将我们存储好信息的employee对象返回给controller执行后续代码。

//登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        return Result.success(employeeLoginVO);

登录成功后,借助我们获取到的employee对象,我们借助jwtUtil工具类生成了用于登录校验的token,并将需要返回的对象内容借助employeeLoginVo来存储。以标准的VO来进行返回。

可以看到我们这里对于数据的存储方式不在是new一个对象来挨个调用getset方法存储了,而是直接选择用.builder()来实现。借助 .+参数名(获取到信息的对象.方法)的格式来快速实现数据存储。

这样的方法有两个需要注意的点:

在存储完所有信息只有要加上.build();来结尾。

在存储对象employeeLoginVo中,需要有@Builder注解才可以调用这个build方法。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "员工登录返回的数据格式")
public class EmployeeLoginVO implements Serializable {

    @ApiModelProperty("主键值")
    private Long id;

    @ApiModelProperty("用户名")
    private String userName;

    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("jwt令牌")
    private String token;

}

至此,登陆方法结束。

后端环境搭建_前后端联调_Nginx反向代理和负载均衡

我们需要思考一个问题:

前端发送的请求是如何到达后端的呢?

我们会发现,我们前端和后端的请求路径url并不是相同的,二者并没有匹配到

实际上呢,是nginx反向代理在发力:

Nginx反向代理和负载均衡的具体实现步骤:   

在这个配置后,请求路径会被替换然后传输给tomcat服务器

location的意思是需要处理的特殊请求。下图中是表示对包含有api的请求进行特殊处理。

这负载均衡跟反向代理的区别是什么呢?

主要是在上文中的webservers字段,这个字段一个在upstream后,一个在路径当中。这个字段是不固定的,但要保证这两个位置的字段要完全相同

这样接收到的请求就会以某种策略来传输给这两个服务器中了。

开发环境搭建_完善登录功能

代码实现如下:

 //密码比对
        //进行md5加密,然后再进行比对
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

导入接口文档

项目当中使用Yapi来进行对接口文档的保存和使用。但是2025年发现yapi已经无法使用了。本人使用的是Apifox

里面的接口也都可以直接使用

Swagger_介绍和使用方式

maven坐标如下

<!--对swagger封装-->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>${knife4j}</version>
            </dependency>

在配置类中配置knife4j的配置内容如下:

    /**
     * 通过knife4j生成接口文档
     * @return
     */
    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

在配置类中设置资源映射如下:

  /**
     * 设置静态资源映射
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

在这里,如果我们访问到/doc.html结尾的路径,就可以到我们的接口文档控制台中去了

我们发现,在swagger当中也可以实现接口文档的测试功能,那么:

Swagger_常用注解

没什么好讲的,当一个开发规范用就行,在对应的地方上写对应的注解,你的swagger上就可以生成出规范的接口文档。