day02_前后端环境搭建(前端工程搭建,登录功能说明,后端项目搭建)

文章目录

  • 1. 软件开发介绍
    • 1.1 软件开发流程
    • 1.2 角色分工
    • 1.3 软件环境
    • 1.4 系统的分类
  • 2. 尚品甄选项目介绍
    • 2.1 电商基本概念
      • 2.1.1 电商简介
      • 2.1.2 电商模式
        • B2B
        • B2C
        • B2B2C
        • C2B
        • C2C
        • O2O
    • 2.2 业务功能介绍
    • 2.3 系统架构介绍
    • 2.4 前后端分离开发
  • 3. 前端工程搭建
    • 3.1 Element-Admin简介
    • 3.2 Element-Admin部署
    • 3.3 项目目录介绍
    • 3.4 修改项目基本信息
      • 3.4.1 修改项目默认图标
        • 3.4.1.1 修改标题图标
        • 3.4.1.2 修改侧边栏图标
      • 3.4.2 修改标题
        • 3.4.2.1 标题栏标题
        • 3.4.2.2 侧边栏标题
  • 4. 登录功能说明
    • 4.1 登录流程介绍
    • 4.2 模板源码分析
      • 4.2.1 登录请求过程
        • 登录页面
        • login.js
        • request.js
        • mock
      • 4.2.2 响应结果分析
        • app.js
        • storage.js
    • 4.3 总结
  • 5. 后端项目搭建
    • 5.1 数据库环境准备
      • 5.1.1 安装Mysql数据库
      • 5.1.2 初始化数据库
      • 5.1.3 表结构介绍
      • 5.1.4 部署Redis
    • 5.2 项目结构说明
    • 5.3 模块依赖说明
    • 5.4 项目模块创建
      • 5.4.1 spzx-parent
      • 5.4.2 spzx-common
      • 5.4.3 common-util
      • 5.4.4 spzx-model
      • 5.4.5 common-service
      • 5.4.6 spzx-manager
    • 5.5 统一结果实体类
      • 5.5.1 响应结果分析
      • 5.5.2 Result实体类
    • 5.6 登录接口开发
      • 5.6.1 基础环境配置
      • 5.6.2 SysUser
      • 5.6.3 LoginDto
      • 5.6.4 LoginVo
      • 5.6.5 IndexController
      • 5.6.6 SysUserService
      • 5.6.7 SysUserMapper
      • 5.6.8 SysUserMapper.xml
    • 5.7 Postman测试

1. 软件开发介绍

作为一名软件开发工程师,我们需要了解在软件开发过程中的开发流程, 以及软件开发过程中涉及到的岗位角色,角色的分工、职责, 并了解软件开发中涉及到的三种软件环境。那么本章节,我们将从软件开发流程、角色分工、软件环境 三个方面,来整体上介绍一下软件开发。

1.1 软件开发流程

软件的开发流程如下所示:

在这里插入图片描述

第1阶段: 需求分析

完成产品原型、需求规格说明书的编写。

产品原型: 一般是通过网页(html)的形式展示当前的页面展示什么样的数据, 页面的布局是什么样子的,点击某个菜单,打开什么页面,点击某个按钮,出现什么效果,都可以通过产品原型看到。 可以通过墨刀网查看产品原型样例:https://modao.cc/community

需求规格说明书: 一般来说就是使用 Word 文档来描述当前项目有哪些功能,每一项功能的需求及业务流程是什么样的,都会在文档中描述。

第2阶段: 设计

设计的内容包含 产品设计、UI界面设计、概要设计、详细设计、数据库设计

在设计阶段,会出具相关的UI界面、及相关的设计文档。比如数据库设计,需要设计当前项目中涉及到哪些数据库,每一个数据库里面包含哪些表,这些表结构之间的关系是什么样的,表结构中包含哪些字段,字段类型都会在文档中描述清楚。

第3阶段: 编码

编写项目代码、并完成单元测试。

作为软件开发工程师,我们主要的工作就是在该阶段, 对分配给我们的模块功能,进行编码实现。编码实现完毕后,进行单元测试,单元测试通过后再进入到下一阶段。

第4阶段: 测试

在该阶段中主要由测试人员, 对部署在测试环境的项目进行功能测试, 并出具测试报告。

第5阶段: 上线运维

在项目上线之前, 会由运维人员准备服务器上的软件环境安装、配置, 配置完毕后, 再将我们开发好的项目,部署在服务器上运行。

我们作为软件开发工程师, 我们主要的任务是在编码阶段, 但是在一些小的项目组当中, 也会涉及到数据库的设计、测试等方面的工作。

1.2 角色分工

学习了软件开发的流程之后, 我们还有必要了解一下在整个软件开发过程中涉及到的岗位角色,以及各个角色的职责分工。

在这里插入图片描述

如下表所示:

岗位/角色职责/分工
项目经理对整个项目负责,任务分配、把控进度
产品经理进行需求调研,输出需求调研文档、产品原型等
UI设计师根据产品原型输出界面效果图
架构师项目整体架构设计、技术选型等
开发工程师功能代码实现
测试工程师编写测试用例,输出测试报告
运维工程师软件环境搭建、项目上线

上述我们讲解的角色分工, 是在一个项目组中比较标准的角色分工, 但是在实际的项目中, 有一些项目组由于人员配置紧张, 可能并没有专门的架构师或测试人员, 这个时候可能需要有项目经理或者程序员兼任。

1.3 软件环境

在我们日常的软件开发中,会涉及到软件开发中的三套环境, 那么这三套环境分别是: 开发环境、测试环境、生产环境。 接下来,我们分别介绍一下这三套环境的作用和特点。

开发环境(development)

我们作为软件开发人员,在开发阶段使用的环境,就是开发环境,一般外部用户无法访问。

比如,我们在开发中使用的MySQL数据库和其他的一些常用软件,我们可以安装在本地, 也可以安装在一台专门的服务器中, 这些应用软件仅仅在软件开发过程中使用, 项目测试、上线时,我们不会使用这套环境了,这个环境就是开发环境。

测试环境(testing)

当软件开发工程师,将项目的功能模块开发完毕,并且单元测试通过后,就需要将项目部署到测试服务器上,让测试人员对项目进行测试。那这台测试服务器就是专门给测试人员使用的环境, 也就是测试环境,用于项目测试,一般外部用户无法访问。

生产环境(production)

当项目开发完毕,并且由测试人员测试通过之后,就可以上线项目,将项目部署到线上环境,并正式对外提供服务,这个线上环境也称之为生产环境。

准生产环境: 对于有的公司来说,项目功能开发好, 并测试通过以后,并不是直接就上生产环境。为了保证我们开发的项目在上线之后能够完全满足要求,就需要把项目部署在真实的环境中, 测试一下是否完全符合要求啊,这时候就诞生了准生产环境,你可以把他当做生产环境的克隆体,准生产环境的服务器配置, 安装的应用软件(JDK、Tomcat、数据库、中间件 …) 的版本都一样,这种环境也称为 “仿真环境”。

注意:由于项目的性质和类型不同,有的项目可能不需要这个环境

1.4 系统的分类

一个项目所包含的系统大致是为了两种:

1、后台系统:给公司的业务人员进行使用

2、前台系统:给普通用户所使用

而每一个系统又可以分为前端和后端:

前端:主要负责进行数据的展示,包含的技术:html、css,JavaScript、vue、element plus、node.js 、vite…

后端:主要负责业务逻辑的处理,包含的技术:Java

2. 尚品甄选项目介绍

尚品甄选是一个电子B2C的电子商务平台。

2.1 电商基本概念

2.1.1 电商简介

电商是指利用互联网技术,将商品信息、交易、支付等业务进行电子化处理,实现线上购买和线下物流配送的商业活动。它将传统的商业活动转化为了电子商务活动,使得消费者可以足不出户地浏览、挑选商品,并通过电子支付方式完成付款,最终实现商品的快速配送。

常见的电商平台:京东、天猫、阿里巴巴、咸鱼、淘宝…

2.1.2 电商模式

电商模式是指电子商务平台和线上商家之间进行业务合作的方式,主要包括以下几种:

B2B

B2B ( Business to Business)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,

完成商务交易的过程。电子商务是现代 B2B marketing的一种具体主要的表现形式。

案例:阿里巴巴1688

B2C

B2C是Business-to-Customer的缩写,而其中文简称为“商对客”。“商对客”是电子商务的一种模式,也就是通常说的直接面向消费者销售产品和服务商

业零售模式。这种形式的电子商务一般以网络零售业为主,主要借助于互联网开展在线销售活动。B2C即企业通过互联网为消费者提供一个新型的购物环境——网上商店,消费者通过网络在网上购物、网上支付等消费行为。

案例:唯品会、乐蜂网、京东

B2B2C

B2B2C是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个B指的是商品或服务的供应商,第二个B指的是从事电子商务的企业,C则是表示消费者。

案例:淘宝

C2B

C2B(Consumer to Business,即消费者到企业),是互联网经济时代新的商业模式。这一模式改变了原有生产者(企业和机构)和消费者的关系,是一种消费者贡献价值(Create Value), 企业和机构消费价值(Consume Value)。

C2B模式和我们熟知的供需模式(DSM, Demand SupplyModel)恰恰相反,真正的C2B 应该先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产。通常情况为消费者根据自身需求定制产品和价格,或主动参与产品设计、生产和定价,产品、价格等彰显消费者的个性化需求,生产企业进行定制化生产。

案例:猪八戒

C2C

C2C即 Customer(Consumer) to Customer(Consumer),意思就是消费者个人间的电子商务行为。比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务。

案例:闲鱼、转转

O2O

O2O即Online To Offline(在线离线/线上到线下),是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台,这个概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可涉及到线下,可以通称为O2O。主流商业管理课程均对O2O这种新型的商业模式有所介绍及关注。

案例:美团、饿了吗、猫眼

2.2 业务功能介绍

后台系统功能:(用户登录、系统管理员列表、角色管理、权限规则管理、商品管理、商品分类、商品规格、…)

线上地址:http://spzx-admin.atguigu.cn/

账号/密码:admin/111111

前台系统功能: (首页商品分类查询、分类模块:分类查询、根据分类查询商品数据、登录功能、用户收货地址管理、购物车模块、订单模块…)

线上地址:http://spzx.atguigu.cn/

2.3 系统架构介绍

后台管理系统后端采用的是单体架构【就是把所有的功能写在同一个项目中】,如下所示:
在这里插入图片描述

前台系统的后端采用的是微服务系统架构【一个模块就是一个独立服务】,如下图所示:
在这里插入图片描述

注意:关于微服务系统架构后面会详细讲解,这里简单了解一下

项目使用技术栈(Spring Boot、Spring Cloud Alibaba、Minio、Redis、Docker、Nginx、MySQL、阿里云短信平台…)

2.4 前后端分离开发

尚品甄选采用的开发模式是前后端分离的开发模式。前后端分离开发是一种软件系统的设计和开发模式,将用户界面、逻辑处理和数据层从一个整体应用程序中分离出来,在前端和后端之间建立规范化接口,使得前端和后端可以独立开发、测试、部署和升级。

如下图所示:
在这里插入图片描述

注意:

1、接口:就是一个http的请求地址,接口就规定了请求方式、请求路径、请求参数、响应结果

2、在当前的前后端分离开发中前端和后端传输的数据是json格式

3. 前端工程搭建

3.1 Element-Admin简介

关于后端管理系统的前端工程,我们没有必要从0~1去开发,可以直接基于Vue3-Element-Admin项目模板进行二次开发,在Vue3-Element-Admin已经提供了一些基础性的功能【登录,首页布局、左侧导航菜单…】。

官网地址:https://huzhushan.gitee.io/vue3-element-admin/v1/guide/

源码仓库地址:https://github.com/huzhushan/vue3-element-admin.git

3.2 Element-Admin部署

步骤如下:将课件资料/day02目录下的vue3-element-admin.rar解压缩到前端工作空间下,并通过VSCode打开项目:
在这里插入图片描述

# 在项目目录下配置淘宝镜像
npm install -g --registry=https://registry.npm.taobao.org
# 启动服务
npm start

修改页面项目bug:

# src目录下permission.js文件:
# 原代码:import { TOKEN } from '@/store/modules/app'
# 更改为如下代码:
import { TOKEN } from '@/pinia/modules/app'

在这里插入图片描述

3.3 项目目录介绍

部署好的前端工程的核心目录结构如下所示:

mock					// 用于测试,模拟后端接口地址
public					// 存储公共的静态资源:图片
src						// 源代码目录,非常重要
    | api				// 提供用于请求后端接口的js文件
    | assets			// 存储静态资源:图片、css
    | components		// 存储公共组件,可重用的一些组件
    | directive			// 存储自定义的一些指令
    | hooks				// 存储自定义的钩子函数
    | i18n				// 存储用于国际化的js文件
    | layout			// 存储首页布局组件
    | pinia				// 用于进行全局状态管理
    | router			// 存储用于进行路由的js文件
    | utils				// 存储工具类的js文件
    | views				// 和路由绑定的组件
    | App.vue			// 根组件
    | default-settings.js // 默认设置的js文件
    | error-log.js		// 错误日志js文件
    | global-components.js // 全局组件的js文件
    | main.js			// 入口js文件(非常重要)
    | permission.js		// 权限相关的js文件(路由前置守卫、路由后置守卫)
vite.config.js			// vite的配置文件,可以在该配置文件中配置前端工程的端口号

3.4 修改项目基本信息

3.4.1 修改项目默认图标

拷贝课件 资料/day02/favicon.ico 到前端项目public下

在这里插入图片描述

3.4.1.1 修改标题图标

修改index.html link标签图标地址

<link rel="icon" href="/public/favicon.ico" />
3.4.1.2 修改侧边栏图标

修改src/layout/components/sidebar/Logo.vue页面的图标地址:

<img class="logo" src="/public/favicon.ico" @click="goHome" />

在这里插入图片描述

3.4.2 修改标题

3.4.2.1 标题栏标题

参考下图修改 src/layout//pinia/modules/app.js 中的title

在这里插入图片描述

3.4.2.2 侧边栏标题

修改src/layout/components/sidebar/Logo.vue中的标题

在这里插入图片描述

4. 登录功能说明

我们借助登录功能来了解前后端分离项目整合,并对后端项目进行统一环境配置。

在Element-Admin项目模板中已经将登陆的功能实现了,本小章节就来给大家介绍一下前后端分离项目中如何实现登录功能。

4.1 登录流程介绍

前后端分离开发的登录如下所示:

在这里插入图片描述

注意:

1、令牌:登录成功以后系统给当前登录用户分配的唯一标识

2、前端:前端系统获取到后端返回的令牌的时候就会把令牌存储起来

验证用户是否登录的流程:后端系统中所提供的一些请求地址(接口)中有一些是必须要求用户登录才可以访问。

在这里插入图片描述

Vue3-Element-Admin的登录功能实现:当登录成功以后,会将登录成功以后返回的数据存储到pinia的useApp模块中,同时会把数据存储到localStorage中。

在这里插入图片描述

4.2 模板源码分析

4.2.1 登录请求过程

登录页面

登录页面:src/views/login/index.vue

<script>
import {
  defineComponent,
  getCurrentInstance,
  reactive,
  toRefs,
  ref,
  computed,
  watch,
} from 'vue'
import { Login } from '@/api/login'   // 导入登录发送请求所需要的js文件
import { useRouter, useRoute } from 'vue-router'		// 导入路由组件
import { useApp } from '@/pinia/modules/app'	// 从pinia中导入useApp模块
export default defineComponent({
  name: 'login',
  setup() {
    const route = useRoute()	// 获取当前路由对象
    const state = reactive({
      model: {  // 登录表单默认的用户名和密码
        userName: 'admin',
        password: '123456',
      },
      submit: () => { // 登录方法
        state.loginForm.validate(async valid => {
          if (valid) {  // 校验数据的合法性,合法执行下述代码
            state.loading = true
            const { code, data, message } = await Login(state.model)  // 调用登录方法
            if (+code === 200) {  // 返回的状态码如果为200,给出登录成功提示信息
              ctx.$message.success({
                message: ctx.$t('login.loginsuccess'),	// 读取i18n中locals/zh-cn/login.js中的内容
                duration: 1000,
              })

              const targetPath = decodeURIComponent(route.query.redirect)	
              if (targetPath.startsWith('http')) {
                // 如果是一个url地址
                window.location.href = targetPath
              } else if (targetPath.startsWith('/')) {
                // 如果是内部路由地址
                router.push(targetPath)
              } else {
                router.push('/')  // 登录成功以后进行路由 , 查看src/router/index.js的路由配置
              }
              useApp().initToken(data)  // 保存后端返回给前端的数据
            } else {  // 登录失败给出错误提示信息
              ctx.$message.error(message)
            }
            state.loading = false
          }
        })
      },
    })
    return {
      ...toRefs(state),
    }
  },
})
</script>
login.js

/api/login.js文件源码分析:

import request from '@/utils/request'		// 导入utils中的request.js

// 登录接口
export const Login = data => {
  return request({
    url: '/api/login',		// 请求的后端地址,可以可以将其更改为真实的后端地址
    method: 'post',			// 请求方式
    data,					// 请求的数据
  })
}
request.js

/utils/request.js文件

import axios from 'axios'		// 导入axios
const service = axios.create({	// 创建axios对象
  baseURL: '/',					// 后期需要将这个地址更改为后端真实服务地址
  timeout: 10000,
  withCredentials: true,        //请求时携带cookie=
})
export default service			// 导出axios对象
mock

当前登录请求时通过mock模拟的后端地址,请求的是mock/login.js模拟的后端接口,源码如下所示:

export default [
  {
    url: '/api/login',
    method: 'post',
    timeout: 1000,
    statusCode: 200,
    response: ({ body }) => {
      // 响应内容
      return +body.password === 123456
        ? {
            code: 200,
            message: '登录成功',
            data: {
              token: '@word(50, 100)', // @word()是mockjs的语法
              refresh_token: '@word(50, 100)', // refresh_token是用来重新生成token的
            },
          }
        : {
            code: 400,
            message: '密码错误,请输入123456',
          }
    },
  },
]

4.2.2 响应结果分析

app.js

登录请求成功以后,那么此时会调用useApp().initToken(data) 将后端返回的数据保存起来。useApp导入的是pinia/modules/app.js文件,核心源码如

下所示:

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
import { useAccount } from './account'	// pinia的account模块
import { useTags } from './tags'		// pinia的标签栏模块
import { useMenus } from './menu'		// pinia的menu模块
export const TOKEN = 'VEA-TOKEN'
const COLLAPSE = 'VEA-COLLAPSE'

export const useApp = defineStore('app', {  
  state: () => ({		// 当前pinia模块中所存储的状态数据
    title: 'Vue3 Element Admin',
    authorization: getItem(TOKEN),
    sidebar: {
      collapse: getItem(COLLAPSE),
    },
    device: 'desktop',
  }),
  actions: {  // pinia中定义的方法,用来操作pinia中所存储的数据
    setToken(data) {
      this.authorization = data		// 把后端返回的数据存到useApp模块的authorization变量中
      setItem(TOKEN, data)			// 保存到localStorage
    },
    initToken(data) {	// 初始化token的方法
      this.clearToken()	// 清空token
      this.setToken(data) // 设置token数据
    },
    clearToken() {
      this.authorization = ''	// 将authorization数据设置为空字符串
      removeItem(TOKEN)			// 删除localStorage中的token
      useAccount().clearUserinfo()	// 清除pinia的account模块中存储的用户信息
      useTags().clearAllTags()		// 清除pinia的useTags模块中存储的标签栏数据
      useMenus().setMenus([])		// 清空pinia的menu模块中存储的菜单数据
    },
 
  },
})

storage.js

setItem方法和removeItem方法都是来自于/utils/storage.js中的,源码如下所示:

export const setItem = (name, value) => {		// 设置数据到localStorage中
  if (typeof value === 'object') {	// 判断数据的类型是否为对象类型
    value = JSON.stringify(value)	// 将对象转换成json字符串
  }
  window.localStorage.setItem(name, value)	// 存储数据到localStorage中
}

export const removeItem = name => {		// 从localStorage中删除数据
  window.localStorage.removeItem(name)
}

4.3 总结

综合上面的登录流程和源码分析

前端以post方式提交登录请求访问接口/api/login,后端需要返回json字符串 {"token":"xxx","refresh_token":"xxxx"}代表登录成功的状态。

前端将登录成功的状态缓存到localStorage中。

后续再获取localStorage中的token值访问接口/api/userinfo获取用户登录信息回显,后端返回用户信息的json字符串如下:{id: 1, name: 'zhangsan' , avatar: "xx"}

5. 后端项目搭建

本章节通过开发尚品甄选项目中的登录接口搭建后端环境。

本次项目开发的时候所使用的软件环境版本如下所示:

软件名称版本说明
jdkjdk17
spring boot3.0.5
mybaits-spring-boot-starter3.0.1
mysql8.0.29
idea2022.2.2

5.1 数据库环境准备

5.1.1 安装Mysql数据库

本地安装mysql数据库使用的是docker安装,对应的步骤如下所示:

1、准备一个虚拟机(centos7.x),配置固定ip地址

# 编辑网卡文件
vim /etc/sysconfig/network-scripts/ifcfg-ens33

# 按照一下配置,配置网卡信息,其余不变
ONBOOT=yes
IPADDR=192.168.136.142
GATEWAY=192.168.136.2
NETMASK=255.255.255.0
BOOTPROTO=static
DNS1=114.114.114.114
DNS2=8.8.8.8
# 重启网卡服务
systemctl restart network

# 检测是否可以连接外网
ping www.baidu.com

注意:如果不能连接外网可以尝试添加一块新的虚拟网卡

2、部署mysql

# 拉取镜像
docker pull mysql:8

# 创建容器
docker run -d --name spzx-mysql -p 3306:3306 -v mysql_data:/var/lib/mysql -v mysql_conf:/etc/mysql --restart=always --privileged=true -e MYSQL_ROOT_PASSWORD=123456 mysql:8

5.1.2 初始化数据库

具体步骤如下所示:

1、连接mysql,创建数据库db_spzx

2、导入课程资料中的db_spzx.sql文件

5.1.3 表结构介绍

登录功能相关的表:sys_user【后台管理系统用户表】

CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '会员id',
  `username` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(1:正常 0:停用)',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:未删除 1:已删除)',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';

5.1.4 部署Redis

使用docker部署Redis,具体的操作如下所示:

# 拉取镜像
docker pull redis

# 创建容器
docker run -d -p 6379:6379 --restart=always \
-v spzx-redis-config:/etc/redis/config \
-v spzx-redis-data:/data \
--name spzx-redis redis \
redis-server /etc/redis/config/redis.conf

# 在宿主机的 /var/lib/docker/volumes/redis-config/_data/目录下创建一个redis的配置文件,redis.conf
vim /var/lib/docker/volumes/spzx-redis-config/_data/redis.conf
#内容如下所示
#开启持久化:后面的购物车数据会存到redis中 所以需要保证redis数据的可靠性
appendonly yes
port 6379
requirepass 123456
bind 0.0.0.0

安装课件资料中的redis图形化工具:redis-desktop-manager

连接redis测试

5.2 项目结构说明

尚品甄选的项目结构如下所示:

在这里插入图片描述

模块说明:

spzx-parent: 尚品甄选项目的父工程,进行项目依赖的统一管理,打包方式为pom

spzx-common: 尚品甄选项目公共模块的管理模块,父工程为spzx-parrnt

common-util: 工具类模块,父工程为spzx-common

common-service:公共服务模块,父工程为spzx-common

spzx-model: 尚品甄选实体类模块

spzx-manager: 尚品甄选项目后台管理系统的后端服务

一个项目中所涉及到的实体类往往有三种:

1、封装请求参数的实体类:这种实体类在定义的时候往往会携带到dto【数据传输对象:Data Transfer Object】字样,会定义在dto包中

2、与数据库对应的实体类:这种实体类往往和数据表名称保证一致,会定义在domain、entity、pojo包中

3、封装响应结果的实体类:这种实体类在定义的时候往往会携带到vo【视图对象:View Object】字样,会定义在vo包中

5.3 模块依赖说明

模块之间的依赖关系如下图所示:

在这里插入图片描述

common-service会依赖:common-util、spzx-common

spzx-manager会依赖:common-service

5.4 项目模块创建

5.4.1 spzx-parent

创建一个maven项目,导入如下依赖:

<!-- 指定父工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.5</version>
</parent>
<packaging>pom</packaging>
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <mysql.verison>8.0.29</mysql.verison>
    <fastjson.version>1.2.29</fastjson.version>
    <jodatime.version>2.10.1</jodatime.version>
    <lombok.version>1.18.20</lombok.version>
    <mybatis.version>3.0.1</mybatis.version>
    <pagehelper.version>1.3.0</pagehelper.version>
    <hutool.version>5.5.2</hutool.version>
    <minio.version>8.5.2</minio.version>
    <easyexcel.version>3.1.0</easyexcel.version>
    <logback.version>1.4.6</logback.version>
</properties>

<!-- 管理依赖,版本锁定 -->
<dependencyManagement>

    <dependencies>

        <!-- mybatis和spring boot整合的起步依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- hutool工具包:包含了常用的工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>${easyexcel.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!--日期时间工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>${jodatime.version}</version>
        </dependency>

        <!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelper.version}</version>
        </dependency>
    </dependencies>

</dependencyManagement>

注意:

1、设置项目的字符集为utf-8

2、删除src目录

5.4.2 spzx-common

在spzx-parent下面创建该子模块,并导入如下依赖:

<dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
</dependencies>

5.4.3 common-util

在spzx-common下面创建该子模块,并导入如下依赖:

<dependencies>

    <!-- fastjson依赖 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>

</dependencies>

5.4.4 spzx-model

在spzx-parent下面创建该子模块,并导入如下依赖:

<dependencies>

    <!-- lombok的依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

</dependencies>

5.4.5 common-service

在spzx-common下面创建该子模块,并导入如下依赖:util/model

<dependencies>
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>spzx-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>common-util</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

5.4.6 spzx-manager

在spzx-parent下面创建该子模块,并导入如下依赖:

<dependencies>

    <!-- spring boot web开发所需要的起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- redis的起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- mybatis的起步依赖 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>

    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- common-service依赖 -->
    <dependency>
        <groupId>com.atguigu.spzx</groupId>
        <artifactId>common-service</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

</dependencies>

5.5 统一结果实体类

5.5.1 响应结果分析

通过对前端登录功能的源码查看,可以看到登录请求成功以后,前端需要从返回结果中解析出来三部分的数据:

// code:自定义的业务状态码,前端会根据具体的业务状态码给出不同的处理。比如:200表示成功、非200都是失败
// message:响应消息。比如:登录成功、用户名或者密码错误、用户无权限访问
// data:后端返回给前端的业务数据
const { code, data, message } = await Login(state.model)

5.5.2 Result实体类

针对上述的三部分的数据,我们可以定义一个实体类来进行封装,后期尚品甄选项目中所有的接口的返回值统一都会定义为Result。

spzx-model模块中定义Result实体类,内容如下所示:

// com.atguigu.spzx.model.vo.common
@Data
public class Result<T> {
    //状态码 200代表成功
    private Integer code;
    //状态码的描述
    private String message;
    //成功的响应数据
    private T data;
    //私有化构造器
    private Result(){}
    //手动指定状态码信息构建 result对象
    public static <T> Result<T> setResult(Integer code , String message , T data){
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setData(data);
        result.setMessage(message);
        return result;
    }
    //通过枚举对象构建 result对象
    public static <T> Result<T> setResult(ResultCodeEnum codeEnum , T data){
        return setResult(codeEnum.getCode() , codeEnum.getMessage() , data);
    }
    //默认成功
    public static<T> Result<T> ok(){
        return setResult(ResultCodeEnum.SUCCESS , null);
    }
    //默认失败
    public static<T> Result<T> error(){
        return setResult(ResultCodeEnum.ERROR , null);
    }
    //链式调用设置属性值
    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }
    public Result<T> message(String message){
        this.setMessage(message);
        return this;
    }
    public Result<T> data(T data){
        this.setData(data);
        return this;
    }
}

为了简化Result对象的构造,可以定义一个枚举类,在该枚举类中定义对应的枚举项来封装code、message的信息,如下所示:

// com.atguigu.spzx.model.vo.common
@Getter // 提供获取属性值的getter方法
public enum ResultCodeEnum {

    SUCCESS(200 , "操作成功") ,
    LOGIN_ERROR(201 , "用户名或者密码错误");

    private Integer code ;      // 业务状态码
    private String message ;    // 响应消息

    private ResultCodeEnum(Integer code , String message) {
        this.code = code ;
        this.message = message ;
    }

}

5.6 登录接口开发

5.6.1 基础环境配置

在spzx-manager服务的src/resources目录下创建application.yml、application-dev.yml文件,文件的内容如下所示:

# application.yml文件内容==================================================================================
spring:
  application:
    name: service-manager
  profiles:
    active: dev		# 激活的环境文件
#  application-dev.yml文件内容=============================================================================
# 配置服务端口号
server:
  port: 8501

# 配置数据库连接信息
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.136.142:3306/db_spzx?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
  # Redis的相关配置
  data:
    redis:
      host: 192.168.136.142
      port: 6379
      password: 123456

# mybatis的配置
mybatis:
  config-location: classpath:/mybatis-config.xml
  mapper-locations: classpath:/mapper/*.xml

导入课程资料中提供的:mybatis-config.xml和logback-spring.xml配置文件

启动类创建

@SpringBootApplication
public class SpzxManagerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpzxManagerApplication.class , args) ;
    }

}

5.6.2 SysUser

创建与数据库表对应的实体类:

BaseEntity:

每张表都有一些固定字段,所以定义一个BaseEntity实体类,在该实体类中定义公共的属性

// com.atguigu.spzx.model.entity.base
@Data
public class BaseEntity implements Serializable {

    private Long id;
    private Date createTime;
    private Date updateTime;
    private Integer isDeleted;
    
}

SysUser实体类定义:

// com.atguigu.spzx.model.entity.system
@Data
public class SysUser extends BaseEntity {

	private static final long serialVersionUID = 1L;
	private String userName;  // 该字段的属性名称和数据表字段不一致
	private String password;
	private String name;
	private String phone;
	private String avatar;
	private String description;
	private Integer status;

}

5.6.3 LoginDto

创建一个LoginDto实体类,封装登录请求参数。

// com.atguigu.spzx.model.dto.system
@Data
public class LoginDto {

    private String userName ;
    private String password ;

}

5.6.4 LoginVo

创建一个LoginVo实体类,封装登录成以后响应结果数据。

//com.atguigu.spzx.model.vo.system;
@Data
public class LoginVo {
    private String token ;
    // 该字段不会存储对应的值
    private String refresh_token ;		

}

5.6.5 IndexController

表现层代码实现

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value = "/admin/system/index")
public class IndexController {

    @Autowired
    private SysUserService sysUserService ;

    @PostMapping(value = "/login")
    public Result<LoginVo> login(@RequestBody LoginDto loginDto) {
        LoginVo loginVo = sysUserService.login(loginDto) ;
        return Result.ok().data(loginVo) ;
    }

}

5.6.6 SysUserService

业务层代码实现

// com.atguigu.spzx.manager.service
public interface SysUserService {

    /**
     * 根据用户名查询用户数据
     * @return
     */
    public abstract LoginVo login(LoginDto loginDto) ;

}

// com.atguigu.spzx.manager.service.impl
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper ;

    @Autowired
    private RedisTemplate<String , String> redisTemplate ;

    @Override
    public LoginVo login(LoginDto loginDto) {

        // 根据用户名查询用户
        SysUser sysUser = sysUserMapper.selectByUserName(loginDto.getUserName());
        if(sysUser == null) {
            throw new RuntimeException("用户名或者密码错误") ;
        }

        // 验证密码是否正确
        String inputPassword = loginDto.getPassword();
        String md5InputPassword = DigestUtils.md5DigestAsHex(inputPassword.getBytes());
        if(!md5InputPassword.equals(sysUser.getPassword())) {
            throw new RuntimeException("用户名或者密码错误") ;
        }

        // 生成令牌,保存数据到Redis中
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set("user:login:" + token , JSON.toJSONString(sysUser) , 30 , TimeUnit.MINUTES);

        // 构建响应结果对象
        LoginVo loginVo = new LoginVo() ;
        loginVo.setToken(token);
        loginVo.setRefresh_token("");

        // 返回
        return loginVo;
    }

}

5.6.7 SysUserMapper

持久层代码实现

//com.atguigu.spzx.manager.mapper
@Mapper
public interface SysUserMapper {

    /**
     * 根据用户名查询用户数据
     * @param userName
     * @return
     */
    public abstract SysUser selectByUserName(String userName) ;

}

5.6.8 SysUserMapper.xml

创建映射文件并且编写sql语句: 文件位置classpath: /mapper/system/SysUserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.SysUserMapper">

    <!-- 用于select查询公用抽取的列 -->
    <sql id="columns">
        id,username userName ,password,name,phone,avatar,description,status,create_time,update_time,is_deleted
    </sql>

    <select id="selectByUserName" resultType="com.atguigu.spzx.model.entity.system.SysUser">
        select <include refid="columns" /> from sys_user where username = #{userName} and is_deleted = 0
    </select>

</mapper>

5.7 Postman测试

使用Postman工具先对后端所开发的接口进行测试。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/414732.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

漫漫数学之旅034

文章目录 经典格言数学习题古今评注名人小传 - 大卫希尔伯特 经典格言 研究数学的艺术在于发现包含普遍性萌芽的特殊情形。——大卫希尔伯特&#xff08;David Hilbert&#xff09; 亲爱的朋友&#xff0c;让我们一起进入数学的奇幻世界&#xff0c;那里大卫希尔伯特就像一位智…

LightSNS V1.6.6.0版轻社区解锁版源码优化版

优化&#xff1a;后台面板首页数据统计改成异步加载 优化&#xff1a;同一内容重复评论提示 优化&#xff1a;vip到期个人主页vip专属背景还保留问题 优化&#xff1a;活动报名名额为空可能导致的问题 优化&#xff1a;移动端评论框左下“转发动态”改为“转发内容” 优化&…

【C++】树形关联式容器set、multiset、map和multimap的介绍与使用

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.关联式容器 2.键…

Linux笔记--用户与用户组

Linux系统是一个多用户多任务的操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员(root)申请一个账号&#xff0c;然后以这个账号的身份进入系统。 用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪&#xff0c;并控制他们对系…

python接口自动化测试 —— unittest框架suite、runner详细使用!

test suite 测试套件&#xff0c;理解成测试用例集一系列的测试用例&#xff0c;或测试套件&#xff0c;理解成测试用例的集合和测试套件的集合当运行测试套件时&#xff0c;则运行里面添加的所有测试用例 test runner 测试运行器用于执行和输出结果的组件 test suite、tes…

【前端素材】推荐优质后台管理系统Salreo平台模板(附源码)

一、需求分析 当我们从多个层次来详细分析后台管理系统时&#xff0c;可以将其功能和定义进一步细分&#xff0c;以便更好地理解其在不同方面的作用和实际运作。 1. 结构层次 在结构层次上&#xff0c;后台管理系统可以分为以下几个部分&#xff1a; a. 辅助功能模块&#…

PostgreSQL:开源巨人的崛起和不可阻挡的发展

PostgreSQL&#xff1a;开源巨人的崛起和不可阻挡的发展 PostgreSQL是一款开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;以其强大的功能和持续的发展势头在数据库领域崭露头角。本文将探讨为什么PostgreSQL的发展势不可挡&#xff0c;从开源精神和强大…

GridView 演示(P28 5.4GridView)

一、目标效果 如下图所示&#xff0c;我们通过 GridView 新建100个子项&#xff0c;每个子项上写有自己的 index。 二、具体实现代码 1. Main.qml import QtQuickWindow {width: 640height: 480visible: truetitle: qsTr("GridView 演示")GridView{anchors.fill…

探索NebulaGraph:一个开源分布式图数据库的技术解析

欢迎关注微信公众号&#xff1a;一休哥助手。多种功能等待你的使用。1. 介绍 NebulaGraph的定位和用途 NebulaGraph是一款开源的分布式图数据库&#xff0c;专注于存储和处理大规模图数据。它的主要定位是为了解决图数据存储和分析的问题&#xff0c;能够处理节点和边数量巨大…

林浩然与杨凌芸的Scala编程历险记:变量与数据类型的魔法对决

林浩然与杨凌芸的Scala编程历险记&#xff1a;变量与数据类型的魔法对决 在Scala世界的梦幻殿堂中&#xff0c;两位英勇的程序员——林浩然和杨凌芸正准备开启一场代码之旅。这次&#xff0c;他们将深入探索Scala王国中的变量奥秘与数据类型丛林。 一、变量声明篇 &#xff0…

【DDD】学习笔记-领域驱动设计体系

从统一语言到限界上下文&#xff0c;从限界上下文到上下文映射&#xff0c;从领域分析建模到领域设计建模&#xff0c;再从领域设计建模到领域实现建模&#xff0c;我将软件架构设计、面向对象设计、场景驱动设计和测试驱动开发有机地融合起来&#xff0c;贯穿于领域驱动设计的…

数据结构:循环队列

一、队列的概念 操作受限的线性表&#xff0c;允许在队列的一端执行入队操作&#xff0c;另一端执行出队操作 先进先出(FIFO) 1.顺序队列 物理结构连续&#xff0c;依赖于数组实现 队列中有一个队头指针和队尾指针&#xff0c;队头指针保存每次要出队的元素&#xff0c;队…

使用Jenkins部署前端Vue项目和后端Java服务

Jenkins安装相关插件&#xff0c;供后续使用&#xff08;Dashboard - Manage Jenkins - Plugins&#xff09; Maven Integration plugin https://plugins.jenkins.io/maven-plugin CloudBees Docker Build and Publish pluginhttps://plugins.jenkins.io/docker-build-publish…

代码随想录算法训练营第64天/最后一天 | 84.柱状图中最大的矩形

今日任务 84.柱状图中最大的矩形 84.柱状图中最大的矩形 - Hard 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾…

Opencv实战(3)详解霍夫变换

霍夫变换 Opencv实战系列指路前文&#xff1a; Opencv(1)读取与图像操作 Opencv(2)绘图与图像操作 文章目录 霍夫变换1.霍夫线变换1.1 原理1.2 HoughLines() 2.霍夫圆变换2.1 原理2.2 HoughCircles() 最基本的霍夫变换是从黑白图像中检测直线(线段) 霍夫变换(Hough Transform…

第三百七十回

文章目录 1. 概念介绍2. 使用方法2.1 获取所有时区2.2 转换时区时间 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享一些好的Flutter站点"相关的内容&#xff0c;本章回中将介绍timezone包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

Unity中URP下实现水体(水面反射)

文章目录 前言一、原理1、法一&#xff1a;使用立方体纹理 CubeMap&#xff0c;作为反射纹理使用2、法二&#xff1a;使用反射探针生成环境反射图&#xff0c;所谓反射的采样纹理 二、实现水面反射1、定义和申明CubeMap2、反射向量需要什么3、计算 N ⃗ \vec{N} N 4、计算 V ⃗…

linux服务器调度数据库的存储过程

1、需要安装数据库的客户端 2、安装sqlplus 3、编写sh脚本 脚本内容如下&#xff1a; 4、设置调度任务

React UI框架Antd 以及 如何按需引入css样式配置(以及过程中各种错误处理方案)

一、react UI框架Antd使用 1.下载模块 npm install antd -S 2.引入antd的样式 import ../node_modules/antd/dist/reset.css; 3.局部使用antd组件 import {Button, Calendar} from antd; import {PieChartTwoTone} from ant-design/icons; {/* 组件汉化配置 */} import l…

[机器视觉]halcon应用实例 边缘检测2

一个学习找边的实例2&#xff0c;用于练习相关算子&#xff0c;这个版本比较简单&#xff0c;少了一些对图像的处理。所以主要是用于练习思路和相关算子。只有用了&#xff0c;练了你在脑子里心才有收获。 边缘检测的步骤图解 代码 *直线找边 *清空屏幕&#xff0c;显式控制图…
最新文章