一、项目概述
核心业务
- 用户登录操作
- 后台首页展现、展现左侧菜单列表
- 完成用户模块的crud–增删改查
- 完成商品分类模块的crud
- 完成商品模块的crud
- 完成商品关联入库、关联查询
- 完成商品图片文件上传
- 完成图片的回显 nginx反向代理
- 完成项目服务器部署 准备3台tomcat服务器 实现负载均衡、搭建tomcat集群
- 实现项目linux 真实环境部署
二 、 各表说明
1.user表说明
2. item_cat商品分类表
菜单
3. 商品表对应的详情表item_desc
4. item
4. 权限表rights
三、后端项目搭建
个别项目创建后,不能识别为maven
在pom里添加maven依赖
1. 创建一个新的项目 jt
2. 编辑pom文件
3. 复制目录
4.修改后端服务器的端口号
server:
port: 8091
5. 流程
controller 要加两个注解
- @CrossOrigin //跨越那个服务器,就在谁的头上加注解
- @RestController
//将该类交给Spring容器管理
@RestController
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
//1.测试后端
@GetMapping("findall")
public List<User> findall(){
return userService.findAll();
}
}
service 接口
public interface UserService {
List<User> findAll();
}
实现类 接口实现类
要加注解
- @Service //将该类交给Spring容器管理
@Service
public class UserServiceImplt implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public List<User> findAll() {
return userMapper.findAll();
}
}
mapper接口
- 在mapper头顶加@Mapper注解
- 或者在启动类上加@Mapperscan
@Mapper
public interface UserMapper {
List<User> findAll();
}
后端项目环境的测试
- 目录
- 后补
什么是VUE
components组件页面
完成用户登录
用户登录的流程
- 用户输入用户名和密码
- 通过vue中的axios发起post请求和参数
- 在UserController中接受用户的请求和参数
- 现将密码进行加密处理 根据参数查询数据
4.1 查询成功有数据 输入正确 有且只有一个数据
4.2.查询失败 没有数据 输入有误
5.登陆成功 则返回token秘钥(传token是为了安全性 )利用SysResult对象返回
首页跳转说明
1. 用户动态登录
目录结构:
controller
service
mapper
补充:idea如何解决代理对象飘红
拓展知识:cookie和session
cookie
“小型类型文件”,为了辨别用户身份,是通过session加密跟踪用户信息,即暂时或永久储存在计算机(本地终端)
- 是一个小型文本文件,存储在本地终端上
- 可以存储用户信息
- 数据类型key-value
- 数据一般采用加密的方式保存
- 数据可以“永久”保存
session
session加密跟踪用户信息,会话期间有效
session和cookie选择
特点:
- 临时用session 永久用cookie
- 对于安全性较高的数据,选用session
- seesion和cookie信息的查看,如图:
2. 实现系统页面的跳转
跳转到系统主页–组件是 home
步骤:
- 导入组件
- 添加跳转路径
- 如图:
引入:
业务需求
说明:用户在未登录的情况下,可直接访问其他页面,没有安全性
所以需要添加校验规则
校验的规则:
1.访问没有token信息,则用户没有登录,则需要跳转登录页面
2.
问题:如何实现每一次的请求都要校验
答:路由导航拦截器
路由导航拦截器
后端业务方法编辑
- 业务接口
1.2 如图: - 权限列表查询的sql(关联查询)
2.1 如图:
但是我们映射的sql语句是不能出现重名的id,需要更改:
也可以选择封装时更改 resultMap
写了箭头函数,写this,代表当前的function函数
业务需求 – 子级路由跳转
实现子集
点击请求后,要求跳转的页面组件 在 home组件空白位置展现—即该组件为home组件的子级
路由语法说明
组成路由的三个部分:
<router-link to="/shopping">商场</router-link>
路由 跳转的关键字<router-view></router-view>
路由的填充位,跳转的页面 --> 展现的位置const routes = [ {path: '/', redirect: '/login'},// redirect:再直接询问 {path: '/login', component: Login}, {path:'/home',component:Home} ]
路由的跳转规则
父子组件跳转
第一步:在home里有个占位符 router-view
代码:
在home组件中
<!-- 定义主页面结构-->
<el-main>
<!-- 定义路由展现页面-->
<router-view></router-view>
</el-main>
第二步:实现父子组件的映射
思路:从home组件跳转到home下的子组件user页面下,代码展现–使用children关键字,前提是,在home里有个占位符 router-view
在index 路由组件中
{path:'/home',component:Home,children:[
{path: '/', component: }
]}
实现跳转user的总结
跳转页面,同时获取用户数据 – 实现方法: 生命周期函数
1.
//利用钩子函数实现数据查询
mounted(){
this.getUserList()
}
2.获取的js
async getUserList(){
//动态获取数据 get请求 携带多个数据 封装成对象 用params
const {data: result} = await this.$http.get('/user/list',{
params: this.queryInfo
})
// 访问失败
if(result.status !== 200) return this.$message.error("用户列表查询失败")
// rows 后讲 多层封装
this.userList = result.data.rows
// total 后讲
this.total = result.data.total
console.log("总记录数:"+this.total)
}
封装的对象queryInfo
queryInfo: { //封装的对象
query: '', //查询:请输入内容
pageNum: 1, //初始化页
pageSize: 20 //定义每页几行
}
业务:分页查询数据
前端使用的对象封装,后端数据库 也应该用PageResult对象封装
前后端数据关联:在后端服务器需要额外的查询总数以及分页的总数
请求类型为get 一共传了三个参数 --> 返回结果有五个 --> 把剩余的两个附带上 --> 业务
数据封装:如图
后端编辑
controller
sql查询
分页sql语法:
select * from user limit 起始位置,查询条数 (含头不包尾)
第一页:
select * from user limit 0,10
第二页:
select * from user limit 10,10
第n页:
select * from user limit (页数-1)*条数,条数
(分数据库 limit不是通用的 sql标准 是通过row 行的方式来查询)
需求: 如果query有参数,则添加like关键字 --> 如何实现??动态sql
<where>
<if test="query != null and query != ''">username like "%"#{query}"%" </if>
</where>
扩展:将多值封装成单值 @Param
总结:
controller
实现类
mapper接口
xml
2. 业务: 用户新增
知识点一:非空校验
知识点二:双向数据绑定
2.1 前端js说明
- 点击事件
<el-button type="primary" @click="addUserBtn">确 定</el-button>
- 函数说明 校验
//校验用户数据 valid表示校验是否通过
addUserBtn(){
this.$refs.addUserRef.validate(async valid => {
//如果校验失败 则停止数据
if(!valid) return
//console.log(this.addUserModel)
const {data: result} = await this.$http.post('/user/addUser',this.addUserModel)
if(result.status !== 200) return this.$message.error("用户新增失败")
this.$message.success("用户新增成功")
//关闭对话框
this.dialogVisible = false
//重新获取用户列表
this.getUserList()
})
}
2.2 后端编辑
- 密码加密处理
- 设定默认状态
- 设定默认时间
- sql语句 insert into value
controller
实现类
mapper.xml
3. 业务:更新数据回显
3.1 js解析
点击修改按钮时,触发作用域插槽(获取当前行的所有数据)–>根据id更新数据 --> 为了获取id 所以使用作用域插槽获取当前行的全部数据
需要注意的是sql语句 只需要写三个值,username phone email
仔细看前端页面
- 路径是resultful形式 所以putmapping注意
- 传参是单值 写@path…
- 只有一个参数的时候,sql会自动按照下表,所以#{写什么都行}
3.2 后端编辑
3.2.1 业务:更新数据回显
xml
3.2.2 业务:实现更新数据回显
3.2.2.1 页面js分析
摁钮
摁钮触发的方法 : 校验 和 根据接口文档封装数据
URL:/user/updateUser
参数类型 是user的js对象 – 属性有三个 id phone email --> 后端用json串接
3.2.2.2 编辑后端
-
查看用户接口文档
-
编辑controller
-
实现类:修改更新时间 updated
-
编辑xml文件 update user set phone = #{phone} ,email = #{email},updated = #{updated} where id = #{id}
4. 业务:删除用户数据
4.1 js页面解析
- 点击删除按钮 会弹出消息确认框
- 触发方法 – > scope.row作用域插槽
- 确认就是confirm 取消就是cancel
4.2 后端编辑
4.2.1 查看接口文档
4.2.1 编辑后端
- url:/user/{id}
- 参数id
- 接口文件:接受单个参数id需要添加@PathVariable 接口方法注意不要重名 deleteByUserId()
- sql : delete from user where id = #{id}
5. 业务:用户状态修改
5.1 前端说明
说明:用户可以修改状态 请求路径是:
更新操作一定要写条件
在js中无法直接识别布尔型数据 所以用一个开关
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
<el-switch v-model="scope.row.status" @change="updateStatus(scope.row)"
active-color="#13ce66" inactive-color="#ff4949">
</el-switch>
</template>
拓展:vue.js作用域插槽
一般在表格数据中,信息不全,如果需要获取全部数据,则需要使用 vue.js作用域插槽
作用域插槽 基本语法:
<template slot-scope="scope">
{{scope.row}}
</template>
其中{{scope.row}} 代表当前行的全部信息
5.2 后端编辑
controller–更新操作不用返回值,看路径是resultful形式,
实现类
5. 业务:异常处理
5.1 spring异常
全局异常的处理机制 – 只拦截controller层抛出的异常 语法规则
- 1.标识该类是异常处理机制
- @RestControllerAdvice 返回对象是json串
- advice:通知 AOP技术 面向切面编程 的技术 用来解决特定问题
- 返回值几乎统一
- 说明:需要定义一个方法
- 要求:返回的是统一的业务数据 SysResult
- 拦截:指定遇到某种异常实现AOP处理
- 2.在方法上添加注解 @ExceptionHandler()
参考:
//全局异常的处理机制 -- 只拦截controller层抛出的异常
/*语法规则
* 1.标识该类是异常处理机制
* @RestControllerAdvice 返回对象是json串
* advice:通知 AOP技术 面向切面编程 的技术 用来解决特定问题
* 返回值几乎统一
* 说明:需要定义一个方法
* 要求:返回的是统一的业务数据 SysResult
* 拦截:指定遇到某种异常实现AOP处理
* 2.在方法上添加注解 @ExceptionHandler()
* */
@RestControllerAdvice
public class SystemExe {
//用来拦截指定的异常 当我们遇到运行异常时,报这个异常
@ExceptionHandler({RuntimeException.class})
public Object fail(Exception e){//异常类型较多 数量多 用大类Object接
e.printStackTrace();
//返回返回失败
return SysResult.fail();
// return SysResult.fail1();
// return SysResult.fail2();
}
}
5.2 spring中事务机制
5.2.1 特征
- 原子性 事务同时成功同时失败 不能分割
- 隔离性 多个事务相互隔离 独立 互不干扰
- 一致性 保证数据的一直(脏读/幻读/不可重复度)
- 持久性 一旦事务提交 就应该持久化保存
5.2.2 spring中默认事务策略
5.2.3 spring添加事务
在实现类上添加注解@Transactional
/* @Transactional 开启spring事务管理机制
* 默认值rollbackFor = RuntimeException.class
* 如果想要他拦截其他异常 需要添加属性 noRollbackFor = RuntimeException.class
* 在写CRUD业务层(除查询)时,写这个注解 */
@Transactional
@Override
public void deleteUserById(Integer id) {
userMapper.deleteUserById(id);
}
6. 补充知识
7. 业务:商品分类展现
7.2.2 优化手段
思路:获取所有的数据库记录,之后按照父子级关系进行封装
结构:Map<k,v>
Map<parentId,当前父级的自己信息> — 只有自己的信息 – 当前查询的信息
封装数据规则:
定义一个结果集是 Map<Integer,List>的方法 实现类中
- 创建一个map集合 Map<Integer,List>
2.1 获取数组中的所有数据,itemCatMapper.selectList(null); 没有查询条件,可以为null
遍历所有的数据,如果存在,追加数据,不存在,则以自己的id创建一个数组,以自己为第一个元素
2.2 遍历数据 for (类型 遍历的每一个元素的名字 :被遍历的对象)
2.3 拿到parentid ----> itemcat.getparentid()
2.4 判断是否存在 if(map.containskey(parentid)){key存在,追加
}else{key不存在,定义list集合,把自己当作第一个元素,存入集合}
2.5 定义list集合 list childrenlist = new … 然后再把自己当作第一个元素存入集合,childrenlist.add() map.put(parentid,childrenlist)
2.6 key存在,追加 map.get(parentid).add(itemcat)追加自己的id和元素
2.7 以后拿子集,写map的k就可以取到了
2.8 最后返回map集合