JAVA项目实战-瑞吉外卖Day1

业务开发Day1

1、软件开发整体介绍

  • 软件开发流程

JAVA项目实战-瑞吉外卖Day1_第1张图片

  • 角色分工

JAVA项目实战-瑞吉外卖Day1_第2张图片

  • 软件环境

JAVA项目实战-瑞吉外卖Day1_第3张图片

从开发人员角度来说,接触最多的就是开发环境。

2、瑞吉外卖项目介绍

  • 项目介绍

本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产品,包括系统管理后台和移动端应用两部分。其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的菜品、套餐、订单等进行管理维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。也就是说,整个项目分为管理员端和客户端两个部分。

本项目共分为3期进行开发:
第一期主要实现基本需求,其中移动端应用通过H5实现,用户可以通过手机浏览器访问。
第二期主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便。
第三期主要针对系统进行优化升级,提高系统的访问性能。

  • 产品原型展示

产品原型就是一款产品成型之前的一个简单的框架,就是将页面的排版布局展现出来,使产品的初步构思有一个可视化的展示。通过原型展示,可以更加直观地了解项目的需求和提供的功能。产品原型是产品经理在需求分析阶段来做的,一般的产品原型都是一些网页(HTML页面)。

注:产品原型主要用于展示项目的功能,并不是最终的页面效果。

  • 技术选型

项目需要用到的技术栈:

JAVA项目实战-瑞吉外卖Day1_第4张图片

  • 功能架构

展示项目需要具有哪些功能

JAVA项目实战-瑞吉外卖Day1_第5张图片

  • 角色

JAVA项目实战-瑞吉外卖Day1_第6张图片

3、开发环境搭建-数据库环境搭建

数据库环境搭建:

创建项目对应的数据库(图形界面或者命令行都可以)

1、首先新建数据库,名称为reggie,字符集是utf-8mb4

JAVA项目实战-瑞吉外卖Day1_第7张图片

2、新建数据库后运行相关SQL文件,导入相关的表结构

项目涉及到了11张表

JAVA项目实战-瑞吉外卖Day1_第8张图片

导入后的表:

JAVA项目实战-瑞吉外卖Day1_第9张图片

3、数据表对照定义(表结构):

JAVA项目实战-瑞吉外卖Day1_第10张图片

4、开发环境搭建-Maven项目搭建

1、创建Maven项目

JAVA项目实战-瑞吉外卖Day1_第11张图片

项目名:reggie_take_out

Maven项目就已经创建成功了。

注:创建完项目后,注意检查项目的编码、maven仓库配置、jdk配置等。

2、导入相关的pom文件,导入所需要的依赖

本地资料有pom文件,直接复制即可。


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.4.5version>
        <relativePath/> 
    parent>
    <groupId>com.itheimagroupId>
    <artifactId>reggie_take_outartifactId>
    <version>1.0-SNAPSHOTversion>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
            <scope>compilescope>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.4.2version>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.20version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.76version>
        dependency>

        <dependency>
            <groupId>commons-langgroupId>
            <artifactId>commons-langartifactId>
            <version>2.6version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.23version>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.4.5version>
            plugin>
        plugins>
    build>

project>

3、导入相关的application.yml文件,对SpringBoot进行配置

本地资料有application.yml文件,这个配置文件可以对SpringBoot进行配置。

server:
  port: 8099
spring:
  application:
    #应用的名称(可选)
    name: reggie_take_out
    #数据源相关的配置
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: root
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

4、编写启动类(SpringBoot项目入口)

//@Slf4j输出日志方便调试,是lombok提供的注解
@Slf4j
@SpringBootApplication
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功!");
    }
}

由于SpringBoot默认静态前端页面是放在static和template目录下的,所以直接将前端资源放在resources目录下去访问是不可行的,此时我们就需要进行配置,使得MVC框架识别到特定文件夹下的资源是我们需要访问的前端资源,此时我们需要写一个配置类。

@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    
    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始进行静态资源映射");
        //将url需要访问的地方映射到相应的文件夹下,这样就不会拒绝访问了
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }
}

注:记得在启动程序加上@ServletComponentScan这个注解,否则这个配置类不会生效;

5、后台登录功能

5.1需求分析

需求分析一般都是从页面原型说起、

页面原型:

JAVA项目实战-瑞吉外卖Day1_第12张图片

登陆页面展示:

当我们输入用户名密码后,点击登录按钮

JAVA项目实战-瑞吉外卖Day1_第13张图片

此时我们可以发现,请求到了employee/login路径下面,并且将用户名和密码,以json格式提交到了服务端,报404是因为后端的接口尚未开发。

需求流程:

JAVA项目实战-瑞吉外卖Day1_第14张图片

数据模型参照employee表

前端代码:

这里可以看到,前端所需要的参数分别有code、data以及msg。它从这个返回结果里获得这些值,就要求我们服务端处理完之后,响应数据里应该含有这三个值,这样前端页面才能够取到,也就是在明确服务端在处理完之后,应该给服务页面响应什么样的结果数据。(数据应该是一个json格式的数据,到时候在写后端代码的时候还会再强调)

JAVA项目实战-瑞吉外卖Day1_第15张图片

5.2代码开发

1、创建实体类Employee,和employee表进行映射,可以直接导入资料中提供的实体类。

package com.itheima.reggie.Entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 员工实体类
 */
@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;

    private Integer status;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

新建controller,entity,service,mapper包全部创建出来,将employee实体类创建出来

JAVA项目实战-瑞吉外卖Day1_第16张图片

然后将后台登陆功能的controller,service,serviceimpl,mapper类全部创建

mapper类:

package com.itheima.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Employee;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
        
}

虽然这部分代码量不大,但由于继承了BaseMapper,所以基本的CRUD功能都在,功能还是十分强大的。

service类:

package com.itheima.reggie.service;

/*
Employee的service接口
*/
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Employee;

public interface EmployeeService extends IService<Employee> {
    
}

这个接口也需要继承MybatisPlus提供的IService接口,也需要指定泛型为Employee实体

service实现类:

package com.itheima.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.mapper.EmployeeMapper;
import com.itheima.reggie.service.EmployeeService;
import org.springframework.stereotype.Service;

//加@Service注解,由Spring管理它
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
    
}

service实现类需要继承ServiceImpl类,其中有两个泛型,分别是EmployeeMapper和Employee实体类,然后实现刚刚定义好的EmployeeService接口。

controller类:

package com.itheima.reggie.controller;

import com.itheima.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    
    @Autowired
    private EmployeeService employeeService;
    
}

在RequestMapping这里,为什么是"/employee"呢?因为登录的时候请求路径里面含有employee,控制层需要和URI对应,然后里面通过@Autowired注解,把咱们创建的service接口注入进来就可以了。

5.3导入通用返回结果类

导入通用返回结果类R,这个类R就是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面。

创建common包,然后将R类复制进去

package com.itheima.reggie.common;

import lombok.Data;
import java.util.HashMap;
import java.util.Map;

@Data
public class R<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

code:代表的是返回值,1代表成功,其它数字代表失败。

msg:代表错误信息,比如登陆失败的时候,msg里面会有显示登陆失败的信息

data:具体的一些数据,比如登录的时候,这个T就可能是employee员工对象,后面在开发的时候还有其他对象,增强通用性

map:封装的一些动态数据,特定场合会使用

5.4梳理登录方法处理逻辑

在控制层中写登录接口的控制层代码:

/**
 * 员工登录
 * @param request
 * @param employee
 * @return
 */
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
    return null;
}

首先定义PostMapping,确定好URI地址,然后返回类是R,泛型是Employee,需要的参数是employee员工类,注意:前端所需的参数是json格式的username和password,所以需要在employee参数前加一个@RequestBody注解。除了员工类,还需要一个参数HttpServletRequest request,要这个参数的原因是登录成功后,需要将employee对象的id存到session一份,表示登陆成功,这样如果想获取当前登录用户的话随时就可以获取出来,就可以通过request对象来get一个session。

JAVA项目实战-瑞吉外卖Day1_第17张图片

注:run和debug的区别。run会直接把一次性的程序全部跑完,debug可以加入断点,来分析程序运行的过程。

5.5实现登录处理逻辑

现在需要做的就是将处理逻辑真正通过代码的方式来实现出来:

1、分析真正处理的逻辑

2、将具体处理的步骤梳理好

3、将梳理好的步骤通过注解的方式写进去,然后逐步实现

@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
    
    // 1、将页面提交的密码password进行md5加密处理
    String password = employee.getPassword();
    //调用DigestUtils工具类,里面有md5加密的工具方法
    password = DigestUtils.md5DigestAsHex(password.getBytes());
    
    // 2、根据页面提交的用户名username查询数据库
    //首先包装一个查询对象
    LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
    //添加查询条件,这里是一个等值查询,根据用户名和传过来的用户名,条件就封装好了
    queryWrapper.eq(Employee::getUsername,employee.getUsername());
    //调用service的getOne方法
    Employee emp = employeeService.getOne(queryWrapper);

    // 3、如果没有查询到则返回登录失败结果
    if (emp == null){
        return R.error("登陆失败!");
    }
    
    // 4、密码比对,如果不一致则返回登录失败结果
    if (!emp.getPassword().equals(password)){
        return R.error("登陆失败,用户名或密码错误!");
    }
    
    // 5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
    if (emp.getStatus() == 0) {
        return R.error("禁止登陆,账号已禁用!");
    }
    
    // 6、登录成功,将员工id存入Session并返回登录成功结果
    request.getSession().setAttribute("employee",emp.getId());
    return R.success(emp);
    
}

调用getOne方法的原因:因为数据库中对username字段做了一个唯一性约束,既然是唯一性约束,就可以调用getOne方法来查出唯一的数据,查出后封装成employee对象

5.6功能测试

在前台页面,进入调试模式,在密码处打了一个断点,然后我们可以查看拿到的数据:可以看到用户名是admin,密码是333333

JAVA项目实战-瑞吉外卖Day1_第18张图片

往下走,可以看到根据用户名查询其实是查到了的

JAVA项目实战-瑞吉外卖Day1_第19张图片

然后发现密码比对处的逻辑判断,密码是错误的了,所以就返回了R.error登陆失败。

输入正确的密码登录进去以后,这里就转成JSON格式的数据展现在浏览器里面了

在这里插入图片描述

浏览器里输入F12,查看Application里面的Storage的Local Storage,可以看到数据(LocalStorage其实就是浏览器存储的一种方式)

JAVA项目实战-瑞吉外卖Day1_第20张图片

json数据是由SpringMVC将R对象转换成json数据的

6、后台退出功能

6.1需求分析

员工登录成功后,页面跳转到后台系统首页面(backend/index.html),此时会显示当前登录用户的姓名:
如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转回登录页面

点击退出按钮,出现404报错,但其实他是发了一个请求出去的

JAVA项目实战-瑞吉外卖Day1_第21张图片

6.2代码开发

用户点击页面中退出按钮,发送请求,请求地址为/employee/logout,请求方式为POST。
我们只需要在Controller中创建对应的处理方法即可,具体的处理逻辑:
1、清理Session中的用户id
2、返回结果

相应代码如下:

/**
 * 员工退出方法
 * @param request 由于等会要用到session,所以需要request参数
 * @return
 */
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
    //清理Session中保存的当前的员工id
    request.getSession().removeAttribute("employee");
    return R.success("退出成功!");
}

7、分析后台系统首页构成和效果展示方式

下列是index.html内的vue代码,左侧的菜单栏就是由这里定义的,menuList列表里面放着一个个的json,每个json里面都有相应的数据

menuList: [
  // {
  //   id: '1',
  //   name: '门店管理',
  //   children: [
      {
        id: '2',
        name: '员工管理',
        url: 'page/member/list.html',
        icon: 'icon-member'
      },
      {
        id: '3',
        name: '分类管理',
        url: 'page/category/list.html',
        icon: 'icon-category'
      },
      {
        id: '4',
        name: '菜品管理',
        url: 'page/food/list.html',
        icon: 'icon-food'
      },
      {
        id: '5',
        name: '套餐管理',
        url: 'page/combo/list.html',
        icon: 'icon-combo'
      },
      {
        id: '6',
        name: '订单明细',
        url: 'page/order/list.html',
        icon: 'icon-order'
      }
  //   ],
  // },
],

真正展示菜单的是上面的elementUI上面给的组件

 <div v-for="item in menuList" :key="item.id">
                <el-submenu :index="item.id" v-if="item.children && item.children.length>0">
                  <template slot="title">
                    <i class="iconfont" :class="item.icon">i>
                    <span>{{item.name}}span>
                  template>
                  <el-menu-item
                    v-for="sub in item.children"
                    :index="sub.id"
                    :key="sub.id"
                    @click="menuHandle(sub,false)"
                    >
                    <i :class="iconfont" :class="sub.icon">i>
                    <span slot="title">{{sub.name}}span>
                    el-menu-item
                  >
                el-submenu>
                <el-menu-item v-else :index="item.id" @click="menuHandle(item,false)">
                  <i class="iconfont" :class="item.icon">i>
                  <span slot="title">{{item.name}}span>
                el-menu-item>
              div>
      v-for="sub in item.children"
                    :index="sub.id"
                    :key="sub.id"
                    @click="menuHandle(sub,false)"
                    >
                    <i :class="iconfont" :class="sub.icon">i>
                    <span slot="title">{{sub.name}}span>
                    el-menu-item
                  >
                el-submenu>
                <el-menu-item v-else :index="item.id" @click="menuHandle(item,false)">
                  <i class="iconfont" :class="item.icon">i>
                  <span slot="title">{{item.name}}span>
                el-menu-item>
              div>

点击菜单的时候其实就是在切换URL,做到新的效果。

你可能感兴趣的:(java,开发语言,springboot,数据库)