day05_用户管理minIO角色分配(页面制作,查询用户,添加用户,修改用户,删除用户,用户头像,查询所有角色,保存角色数据)

文章目录

  • 1 用户管理
    • 1.1 页面制作
    • 1.2 查询用户
      • 1.2.1 需求说明
      • 1.2.2 后端接口
        • 需求分析
        • SysUser
        • SysUserDto
        • SysUserController
        • SysUserService
        • SysUserMapper
        • SysUserMapper.xml
      • 1.2.3 前端对接
        • 实现思路
        • sysUser.js
        • sysRole.vue
    • 1.3 添加用户
      • 1.3.1 需求说明
      • 1.3.2 页面制作
      • 1.3.3 后端接口
        • SysUserController
        • SysUserService
        • SysUserMapper
        • SysUserMapper.xml
      • 1.3.4 前端对接
        • 实现思路
        • sysUser.js
        • sysRole.vue
    • 1.4 修改用户
      • 1.4.1 需求说明
      • 1.4.2 数据回显
      • 1.4.3 提交修改
        • 后端接口
          • SysUserController
          • SysUserService
          • SysUserMapper
          • SysUserMapper.xml
        • 前端对接
          • sysUser.js
          • sysUser.vue
    • 1.5 删除用户
      • 1.5.1 需求说明
      • 1.5.2 后端接口
        • SysRoleController
        • SysRoleService
        • SysRoleMapper
        • SysRoleMapper.xml
      • 1.5.3 前端对接
        • sysUser.js
        • sysUser.vue
  • 2 用户头像
    • 2.1 需求分析
    • 2.2 文件存储方案
    • 2.3 Minio使用
      • 2.3.1 Minio介绍
      • 2.3.2 MinIO安装
      • 2.3.3 Minio入门
    • 2.4 上传文件接口
      • 2.4.1 FileUploadController
      • 2.4.2 FileUploadService
      • 2.4.3 MinioProperties
      • 2.4.4 配置文件内容
    • 2.5 前端对接
  • 3 分配角色
    • 3.1 需求分析
    • 3.2 页面制作
    • 3.3 查询所有角色
      • 3.3.1 后端接口
        • SysRole
        • SysRoleController
        • SysRoleService
        • SysRoleMapper
        • SysRoleMapper.xml
      • 3.3.2 前端对接
        • sysRole.js
        • sysUser.vue
    • 3.4 保存角色数据
      • 3.4.1 后端接口
        • AssginRoleDto
        • SysRoleUser
        • SysRoleUserController
        • SysRoleUserService
        • SysRoleUserMapper
        • SysRoleUserMapper.xml
      • 3.4.2 前端对接
        • sysUser.js
        • sysUser.vue
      • 3.4.2 前端对接
        • sysUser.js
        • sysUser.vue

1 用户管理

用户管理就是对后台管理系统的使用用户进行维护。

1.1 页面制作

对比如下页面结构,使用Element Plus制作出对应的页面,数据可以暂时使用假数据。

在这里插入图片描述

该页面可以将其分为4部分:

1、搜索表单

2、添加按钮

3、数据展示表格

4、分页条组件

代码实现如下所示:

<template><!---搜索表单--><div class="search-div"><el-form label-width="70px" size="small"><el-row><el-col :span="12"><el-form-item label="关键字"><el-inputstyle="width: 100%"placeholder="用户名、姓名、手机号码"></el-input></el-form-item></el-col><el-col :span="12"><el-form-item label="创建时间"><el-date-pickertype="daterange"range-separator="To"start-placeholder="开始时间"end-placeholder="结束时间"format="YYYY-MM-DD"value-format="YYYY-MM-DD"/></el-form-item></el-col></el-row><el-row style="display:flex"><el-button type="primary" size="small">搜索</el-button><el-button size="small">重置</el-button></el-row></el-form></div><!--添加按钮--><div class="tools-div"><el-button type="success" size="small">添 加</el-button></div><!---数据表格--><el-table :data="list" style="width: 100%"><el-table-column prop="userName" label="用户名" /><el-table-column prop="name" label="姓名" /><el-table-column prop="phone" label="手机" /><el-table-column prop="avatar" label="头像" #default="scope"><img :src="scope.row.avatar" width="50" /></el-table-column><el-table-column prop="description" label="描述" /><el-table-column prop="status" label="状态" #default="scope">{{ scope.row.status == 1 ? '正常' : '停用' }}</el-table-column><el-table-column prop="createTime" label="创建时间" /><el-table-column label="操作" align="center" width="280" ><el-button type="primary" size="small">修改</el-button><el-button type="danger" size="small">删除</el-button><el-button type="warning" size="small">分配角色</el-button></el-table-column></el-table><el-pagination:page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"/></template><script setup>
import { ref } from 'vue'; // 表格数据模型
const list = ref([{"id":1 , "userName":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} ,{"id":2 , "userName":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} 
]);// 分页条数据模型
const total = ref(0)</script><style scoped>
.search-div {margin-bottom: 10px;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
.tools-div {margin: 10px 0;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
</style>
<style scoped>
.avatar-uploader .avatar {width: 178px;height: 178px;display: block;
}
</style><style>
.avatar-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);
}.avatar-uploader .el-upload:hover {border-color: var(--el-color-primary);
}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;
}
</style>

1.2 查询用户

1.2.1 需求说明

需求说明:

1、如果在搜索表单中输入和查询关键字以及创建的开始时间和结束是时间,那么此时就需要按照查询关键字以及创建的开始时间和结束是时间进行条件查询

2、查询关键字搜索的字段可以是用户名、姓名、手机号码。在查询的时候需要继续按照这些字段进行模糊查询。

2、搜索的时候需要进行分页搜索

1.2.2 后端接口

需求分析

1、前端提交请求参数的时候包含了两部分的参数:搜索条件参数、分页参数。搜索条件参数可以通过?拼接到请求路径后面,分页参数【当前页码、每页显示的数据条数】可以让前端通过请求路径传递过来

2、后端查询完毕以后需要给前端返回一个分页对象,分页对象中就封装了分页相关的参数(当前页数据、总记录数、总页数…)

3、前端进行参数传递的时候,不一定会传递搜索条件,因此sql语句的编写需要使用到动态sql

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;}
SysUserDto

定义一个实体类用来封装前端所传递过来的查询参数,具体定义如下所示:

// com.atguigu.spzx.model.dto.system
@Data
public class SysUserDto {private String keyword ;private String createTimeBegin ;private String createTimeEnd;}
SysUserController

表现层代码:

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value = "/admin/system/sysUser")
public class SysUserController {@Autowiredprivate SysUserService sysUserService ;@GetMapping(value = "/findByPage/{pageNum}/{pageSize}")public Result<PageInfo<SysRole>> findByPage(SysUserDto sysUserDto ,@PathVariable(value = "pageNum") Integer pageNum ,@PathVariable(value = "pageSize") Integer pageSize) {PageInfo<SysUser> pageInfo = sysUserService.findByPage(sysUserDto , pageNum , pageSize) ;return Result.build(pageInfo , ResultCodeEnum.SUCCESS) ;}}
SysUserService

业务层代码实现

// com.atguigu.spzx.manager.service.impl
@Override
public PageInfo<SysUser> findByPage(SysUserDto sysUserDto, Integer pageNum, Integer pageSize) {PageHelper.startPage(pageNum , pageSize);List<SysUser> sysUserList = sysUserMapper.findByPage(sysUserDto) ;PageInfo pageInfo = new PageInfo(sysUserList) ;return pageInfo;
}
SysUserMapper

持久层代码实现

@Mapper
public interface SysUserMapper {public abstract List<SysUser> findByPage(SysUserDto sysUserDto);
}
SysUserMapper.xml

在映射文件中定义对应的sql语句

<sql id="findPageWhere"><where><if test="keyword != null and keyword != ''">and (username like CONCAT('%',#{keyword},'%') or name like CONCAT('%',#{keyword} , '%') or phone like CONCAT('%',#{keyword} , '%'))</if><if test="createTimeBegin != null and createTimeBegin != ''">and create_time >= #{createTimeBegin}</if><if test="createTimeEnd != null and createTimeEnd != ''">and create_time &lt;= #{createTimeEnd}</if>and is_deleted = 0</where>
</sql><select id="findByPage" resultType="com.atguigu.spzx.model.entity.system.SysUser" >select <include refid="columns" />from sys_user<include refid="findPageWhere"/>order by id desc
</select>

1.2.3 前端对接

实现思路

如下所示:

1、定义发送请求方法

2、搜索表单绑定对应数据模型

3、onMounted钩子函数发送请求查询数据

4、分页条绑定数据模型以及对应事件

sysUser.js

在api目录下创建一个sysUser.js文件,文件的内容如下所示:

import request from '@/utils/request'// 分页查询
export const GetSysUserListByPage = (pageNum , pageSize , queryDto) => {return request({url: "/admin/system/sysUser/findByPage/" + pageNum + "/" + pageSize,method: 'get',params: queryDto})
}
sysRole.vue

更改views/system/sysRole.vue文件

<!-- 搜索表单 -->
<!---搜索表单-->
<div class="search-div"><el-form label-width="70px" size="small"><el-row><el-col :span="12"><el-form-item label="关键字"><el-input v-model="queryDto.keyword"style="width: 100%"placeholder="用户名、姓名、手机号码"></el-input></el-form-item></el-col><el-col :span="12"><el-form-item label="创建时间"><el-date-picker v-model="createTimes"type="daterange"range-separator="To"start-placeholder="开始时间"end-placeholder="结束时间"format="YYYY-MM-DD"value-format="YYYY-MM-DD"/></el-form-item></el-col></el-row><el-row style="display:flex"><el-button type="primary" size="small" @click="searchSysUser">搜索</el-button><el-button size="small" @click="resetData">重置</el-button></el-row></el-form>
</div>  <!---数据表格-->
<el-table :data="list" style="width: 100%"><el-table-column prop="userName" label="用户名" /><el-table-column prop="name" label="姓名" /><el-table-column prop="phone" label="手机" /><el-table-column prop="avatar" label="头像" #default="scope"><img :src="scope.row.avatar" width="50" /></el-table-column><el-table-column prop="description" label="描述" /><el-table-column prop="status" label="状态" #default="scope">{{ scope.row.status == 1 ? '正常' : '停用' }}</el-table-column><el-table-column prop="createTime" label="创建时间" /><el-table-column label="操作" align="center" width="280" ><el-button type="primary" size="small">修改</el-button><el-button type="danger" size="small">删除</el-button><el-button type="warning" size="small">分配角色</el-button></el-table-column>
</el-table><el-paginationv-model:current-page="pageParams.page"v-model:page-size="pageParams.limit":page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"/><script setup>import { ref , onMounted } from 'vue'; import { GetSysUserListByPage } from '@/api/sysUser';// 表格数据模型const list = ref([{"id":1 , "username":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} ,{"id":2 , "username":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} ]);// 分页条数据模型const total = ref(0)// 定义搜索表单数据模型const queryDto = ref({keyword: "" ,createTimeBegin: "",createTimeEnd: ""})const createTimes = ref([])//分页数据const pageParamsForm = {page: 1, // 页码limit: 10, // 每页记录数}const pageParams = ref(pageParamsForm)// onMounted钩子函数onMounted(() => {fetchData() ;})// 搜素按钮点击事件处理函数const searchSysUser = () => {fetchData()}// 重置按钮点击事件处理函数const resetData = () => {queryDto.value = {}createTimes.value = []}// 定义分页查询方法const fetchData = async () => {if (createTimes.value.length == 2) {queryDto.value.createTimeBegin = createTimes.value[0]queryDto.value.createTimeEnd = createTimes.value[1]}// 请求后端接口进行分页查询const { code , message , data } = await GetSysUserListByPage(pageParams.value.page , pageParams.value.limit , queryDto.value)list.value = data.listtotal.value = data.total}</script>

1.3 添加用户

1.3.1 需求说明

当用户点击添加按钮的时候,那么此时就弹出对话框,在该对话框中需要展示添加用户表单。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要提交过来的表单数据保存到数据库中即可。页面效果如下所示:

在这里插入图片描述

1.3.2 页面制作

页面代码如下所示:

<!--添加按钮-->
<div class="tools-div"><el-button type="success" size="small" @click="addShow">添 加</el-button>
</div><el-dialog v-model="dialogVisible" title="添加或修改" width="40%"><el-form label-width="120px"><el-form-item label="用户名"><el-input /></el-form-item><el-form-item label="密码"><el-input /></el-form-item><el-form-item label="姓名"><el-input /></el-form-item><el-form-item label="手机"><el-input /></el-form-item><el-form-item label="头像"><el-uploadclass="avatar-uploader"action="http://localhost:8501/admin/system/fileUpload":show-file-list="false"><img v-if="sysUser.avatar" :src="sysUser.avatar" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="描述"><el-input  /></el-form-item><el-form-item><el-button type="primary" >提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog> <script setup>// 添加表单对话框显示隐藏控制变量const dialogVisible = ref(false)const addShow = () => {dialogVisible.value = true }// 定义提交表单数据模型const defaultForm = {avatar: ""}const sysUser = ref(defaultForm)
</script>

1.3.3 后端接口

SysUserController

表现层代码

// com.atguigu.spzx.manager.controller.SysUserController
@PostMapping(value = "/sysUser")
public Result saveSysUser(@RequestBody SysUser sysUser) {sysUserService.saveSysUser(sysUser) ;return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysUserService

业务层代码

// com.atguigu.spzx.manager.service.impl.SysUserServiceImpl
@Override
public void saveSysUser(SysUser sysUser) {// 根据输入的用户名查询用户SysUser dbSysUser = sysUserMapper.findByUserName(sysUser.getUserName()) ;if(dbSysUser != null) {throw new GuiguException(ResultCodeEnum.USER_NAME_IS_EXISTS) ;}// 对密码进行加密String password = sysUser.getPassword();String digestPassword = DigestUtils.md5DigestAsHex(password.getBytes());sysUser.setPassword(digestPassword);sysUser.setStatus(0);sysUserMapper.saveSysUser(sysUser) ;
}
SysUserMapper

持久层代码

@Mapper
public interface SysUserMapper {public abstract SysUser findByUserName(String name);public abstract void saveSysUser(SysUser sysUser);
}
SysUserMapper.xml

在映射文件中定义对应的sql语句

<select id="findByUserName" resultType="com.atguigu.spzx.model.entity.system.SysUser">select <include refid="columns" /> from sys_user where username = #{userName}
</select><insert id="saveSysUser">insert into sys_user (id,username,password,name,phone,avatar,description,status) values (#{id},#{userName},#{password},#{name},#{phone},#{avatar},#{description},#{status})
</insert>

1.3.4 前端对接

实现思路

1、给表单绑定数据模型

2、给提交按钮绑定点击事件

3、点击按钮请求后端地址

sysUser.js

在api目录下创建一个sysUser.js文件,文件的内容如下所示:

// 新增用户的方法
export const SaveSysUser = (data) => {return request({url: "/admin/system/sysUser/saveSysUser",method: "post",data})
}
sysRole.vue

更改views/system/sysRole.vue文件,内容如下所示:

<el-dialog v-model="dialogVisible" title="添加或修改" width="40%"><el-form label-width="120px"><el-form-item label="用户名"><el-input v-model="sysUser.userName"/></el-form-item><el-form-item label="密码"><el-input type="password" show-password v-model="sysUser.password"/></el-form-item><el-form-item label="姓名"><el-input v-model="sysUser.name"/></el-form-item><el-form-item label="手机"><el-input v-model="sysUser.phone"/></el-form-item><el-form-item label="头像"><el-uploadclass="avatar-uploader"action="http://localhost:8501/admin/system/fileUpload":show-file-list="false"><img v-if="sysUser.avatar" :src="sysUser.avatar" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="描述"><el-input  v-model="sysUser.description"/></el-form-item><el-form-item><el-button type="primary" @click="submit">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog> <script setup>
import { GetSysUserListByPage , SaveSysUser } from '@/api/sysUser';
import { ElMessage, ElMessageBox } from 'element-plus'// 定义提交表单数据模型
const defaultForm = {userName:"",name: "" ,phone: "" ,password: "" ,description:"",avatar: ""
}
const sysUser = ref(defaultForm)// 提交按钮事件处理函数
const submit = async () => {const {code , message , data} = await SaveSysUser(sysUser.value) if(code === 200) {dialogVisible.value = falseElMessage.success('操作成功')fetchData()}else {ElMessage.error(message)}
}
</script>

1.4 修改用户

1.4.1 需求说明

当用户点击修改按钮的时候,那么此时就弹出对话框,在该对话框中需要将当前行所对应的用户数据在该表单页面进行展示。当用户在该表单中点击提

交按钮的时候那么此时就需要将表单进行提交,在后端需要提交过来的表单数据修改数据库中的即可。页面效果如下所示:

在这里插入图片描述

注意:在进行修改的时候密码框不要进行展示,密码不允许进行修改,可以通过v-if进行控制。

1.4.2 数据回显

分析:

1、使用添加数据的表单即可

2、要将当前操作行的数据展示在表单中,那么此时需要用到插槽

代码如下所示:

<el-table-column label="操作" align="center" width="280" #default="scope" ><el-button type="primary" size="small" @click="editSysUser(scope.row)">修改</el-button>
</el-table-column><script setup>// 修改按钮点击事件处理函数
const editSysUser = (row) => {dialogVisible.value = true sysUser.value = row
}</script>

1.4.3 提交修改

后端接口
SysUserController

表现层代码

// com.atguigu.spzx.manager.controller.SysUserController
@PutMapping(value = "/updateSysUser")
public Result updateSysUser(@RequestBody SysUser sysUser) {sysUserService.updateSysUser(sysUser) ;return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysUserService

业务层代码

// com.atguigu.spzx.manager.service.impl.SysUserServiceImpl
@Override
public void updateSysUser(SysUser sysUser) {sysUserMapper.updateSysUser(sysUser) ;
}
SysUserMapper

持久层代码

// com.atguigu.spzx.manager.mapper.SysUserMapper
public abstract void updateSysUser(SysUser sysUser);
SysUserMapper.xml

映射文件添加如下sql语句:

<update id="updateSysUser">update sys_user set<if test="userName != null and userName != ''">username = #{userName},</if><if test="password != null and password != ''">password = #{password},</if><if test="name != null and name != ''">name = #{name},</if><if test="phone != null and phone != ''">phone = #{phone},</if><if test="description != null and description != ''">description = #{description},</if><if test="status != null and status != ''">status = #{status},</if>update_time =  now()whereid = #{id}
</update>
前端对接
sysUser.js

在api目录下创建一个sysUser.js文件,文件的内容如下所示:

// 修改用户数据的方法
export const UpdateSysUser = (sysUser) => {return request({url: "/admin/system/sysUser/updateSysUser",method: "put",data: sysUser})
}
sysUser.vue

更改views/system/sysUser.vue文件

<script setup>
import { GetSysUserListByPage , SaveSysUser , UpdateSysUser } from '@/api/sysUser';// 提交按钮事件处理函数
const submit = async () => {if(!sysUser.value.id) {const {code , message , data} = await SaveSysUser(sysUser.value) if(code === 200) {dialogVisible.value = falseElMessage.success('操作成功')fetchData()}else {ElMessage.error(message)}}else {const {code , message , data} = await UpdateSysUser(sysUser.value) if(code === 200) {dialogVisible.value = falseElMessage.success('操作成功')fetchData()}else {ElMessage.error(message)}   }}
</script>

1.5 删除用户

1.5.1 需求说明

当点击删除按钮的时候此时需要弹出一个提示框,询问是否需要删除数据?如果用户点击是,那么此时向后端发送请求传递id参数,后端接收id参数进

行逻辑删除。页面效果如下所示:

在这里插入图片描述

1.5.2 后端接口

SysRoleController

表现层代码实现

// com.atguigu.spzx.manager.controller.SysUserController
@DeleteMapping(value = "/deleteById/{userId}")
public Result deleteById(@PathVariable(value = "userId") Long userId) {sysUserService.deleteById(userId) ;return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysRoleService

业务层代码实现

// com.atguigu.spzx.manager.service.impl.SysUserServiceImpl
@Override
public void deleteById(Long userId) {sysUserMapper.deleteById(userId) ;
}
SysRoleMapper

持久层代码实现

// com.atguigu.spzx.manager.mapper.SysUserMapper
public abstract void deleteById(Long userId);
SysRoleMapper.xml

在映射文件中添加如下的sql语句:

<delete id="deleteById">update sys_user setupdate_time = now() ,is_deleted = 1whereid = #{id}
</delete>

1.5.3 前端对接

sysUser.js

在api目录下创建一个sysUser.js文件,文件的内容如下所示:

// 根据id删除用户
export const DeleteSysUserById = (userId) => {return request({url: "/admin/system/sysUser/deleteById/" + userId,method: 'delete'})
}
sysUser.vue

更改views/system/sysRole.vue文件

<el-table-column label="操作" align="center" width="280" #default="scope" ><el-button type="danger" size="small" @click="deleteById(scope.row)">删除</el-button>
</el-table-column><script setup>
import { GetSysUserListByPage , SaveSysUser , UpdateSysUser , DeleteSysUserById} from '@/api/sysUser';
import { ElMessage, ElMessageBox } from 'element-plus'// 删除角色
const deleteById = (row) => {ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(async () => {const {code } = await DeleteSysUserById(row.id)if(code === 200) {ElMessage.success('删除成功')fetchData()}}).catch(() => {ElMessage.info('取消删除')})
}
</script>

2 用户头像

2.1 需求分析

当在进行用户添加的时候,此时可以在添加表单页面点击"+"号,然后选择要上传的用户图像。选择完毕以后,那么此时就会请求后端上传文件接口,

将图片的二进制数据传递到后端。后端需要将数据图片存储起来,然后给前端返回图片的访问地址,然后前端需要将图片的访问地址设置给sysUser数

据模型,当用户点击提交按钮的时候,那么此时就会将表单进行提交,后端将数据保存起来即可。

对应的流程图如下所示:

在这里插入图片描述

2.2 文件存储方案

常见的文件存储方案以及优缺点比较:

方案介绍基本介绍优点缺点
本地文件存储将文件直接存储在服务器本地的硬盘上简单易用,不需要额外的网络设备或服务,可直接读写本地磁盘上的文件。单机存储容量和并发性有限,不具备高可用性和容错能力,可能会影响数据安全和系统稳定性。
分布式文件系统将文件分散存储在多个服务器上,通过网络进行访问和管理。可横向扩展、高可用性、容错能力强,支持大规模数据存储和访问,并提供数据备份和恢复等功能。部署和维护成本较高,对网络通信和硬件环境要求较高。
对象存储服务将文件以对象的形式存储在云服务提供商的存储设施中,并采用HTTP REST API等方式进行访问。可通过HTTP REST API进行访问,具有高可用性、容错能力强,数据安全性好,同时也可以根据实际使用情况灵活调整存储空间和计费方式。上传和下载速度可能受到网络带宽和延迟等因素的影响,并且使用时可能需要付费。

本次我们选择分布式文件系统来存储我们系统中的图片数据。

2.3 Minio使用

2.3.1 Minio介绍

官网:https://www.minio.org.cn/

MinIO是一个开源的分布式对象存储服务器,支持S3协议并且可以在多节点上实现数据的高可用和容错。它采用Go语言开发,拥有轻量级、高性能、易部署等特点,并且可以自由选择底层存储介质。

MinIO的主要特点包括:

1、高性能:MinIO基于GO语言编写,具有高速、轻量级、高并发等性能特点,还支持多线程和缓存等机制进行优化,可以快速地处理大规模数据。

2、可扩展性:MinIO采用分布式存储模式,支持水平扩展,通过增加节点数量来扩展存储容量和性能,支持自动数据迁移和负载均衡。

3、安全性:MinIO提供了多种安全策略,如访问控制列表(ACL)、服务端加密(SSE)、传输层安全性(TLS)等,可以保障数据安全和隐私。

4、兼容性:MinIO兼容AWS S3 API,还支持其他云服务提供商的API,比如GCP、Azure等,可以通过简单的配置实现互操作性。

5、简单易用:MinIO的部署和管理非常简单,只需要运行一个二进制包即可启动服务,同时提供了Web界面和命令行工具等方便的管理工具。

S3协议是Amazon Web Services (AWS) 提供的对象存储服务(Simple Storage Service)的API协议。它是一种 RESTful风格的Web服务接口,使用HTTP/HTTPS协议进行通信,支持多种编程语言和操作系统,并实现了数据的可靠存储、高扩展性以及良好的可用性。

2.3.2 MinIO安装

官网地址:https://www.minio.org.cn/docs/cn/minio/container/index.html

具体命令:

// 创建数据存储目录
mkdir -p ~/minio/data// 创建minio
docker run \-p 9000:9000 \-p 9001:9001 \--name spzx-minio \-v ~/minio/data:/data \-e "MINIO_ROOT_USER=admin" \-e "MINIO_ROOT_PASSWORD=admin123456" \-d \quay.io/minio/minio server /data --console-address ":9001"

2.3.3 Minio入门

本章节会给大家介绍一下如何通过Java客户端操作Minio,可以参考官网地址。

官网地址:https://min.io/docs/minio/linux/developers/java/minio-java.html

具体步骤:

1、加入如下依赖

<!-- common-util模块中加入如下依赖 -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.2</version>
</dependency>

2、示例代码

public class FileUploadTest {public static void main(String[] args) throws Exception {// 创建一个Minio的客户端对象MinioClient minioClient = MinioClient.builder().endpoint("http://192.168.136.142:9001").credentials("admin", "admin123456").build();boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("spzx-bucket").build());// 如果不存在,那么此时就创建一个新的桶if (!found) {minioClient.makeBucket(MakeBucketArgs.builder().bucket("spzx-bucket").build());} else {  // 如果存在打印信息System.out.println("Bucket 'spzx-bucket' already exists.");}FileInputStream fis = new FileInputStream("D://images//1.jpg") ;PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket("spzx-bucket").stream(fis, fis.available(), -1).object("1.jpg").build();minioClient.putObject(putObjectArgs) ;// 构建fileUrlString fileUrl = "http://192.168.136.142:9001/spzx-bucket/1.jpg" ;System.out.println(fileUrl);}}

注意:设置minio的中该桶的访问权限为public,如下所示:

在这里插入图片描述

2.4 上传文件接口

2.4.1 FileUploadController

上传文件的表现层代码:

@RestController
@RequestMapping("/admin/system")
public class FileUploadController {@Autowiredprivate FileUploadService fileUploadService ;@PostMapping(value = "/fileUpload")public Result<String> fileUpload(@RequestParam(value = "file") MultipartFile multipartFile) {String fileUrl = fileUploadService.fileUpload(multipartFile) ;return Result.build(fileUrl , ResultCodeEnum.SUCCESS) ;}}

2.4.2 FileUploadService

上传文件的业务层代码:

@Service
public class FileUploadServiceImpl implements FileUploadService {@Autowiredprivate MinioProperties minioProperties ;@Overridepublic String fileUpload(MultipartFile multipartFile) {try {// 创建一个Minio的客户端对象MinioClient minioClient = MinioClient.builder().endpoint(minioProperties.getEndpointUrl()).credentials(minioProperties.getAccessKey(), minioProperties.getSecreKey()).build();// 判断桶是否存在boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build());if (!found) {       // 如果不存在,那么此时就创建一个新的桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build());} else {  // 如果存在打印信息System.out.println("Bucket 'spzx-bucket' already exists.");}// 设置存储对象名称String extFileName = FileNameUtils.getExtension(multipartFile.getOriginalFilename());String fileName = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID().toString().replace("-" , "") + "." + extFileName;PutObjectArgs putObjectArgs = PutObjectArgs.builder().bucket(minioProperties.getBucketName()).stream(multipartFile.getInputStream(), multipartFile.getSize(), -1).object(fileName).build();minioClient.putObject(putObjectArgs) ;return minioProperties.getEndpointUrl() + "/" + minioProperties.getBucketName() + "/" + fileName ;} catch (Exception e) {throw new RuntimeException(e);}}
}

2.4.3 MinioProperties

将构建MinioClient所对应的参数定义到配置文件中,然后通过该实体类封装该配置文件中的内容。

@Data
@ConfigurationProperties(prefix="spzx.minio") //读取节点
public class MinioProperties {private String endpointUrl;private String accessKey;private String secreKey;private String bucketName;}

2.4.4 配置文件内容

在配置文件中添加Minio的相关配置

# 自定义配置
spzx:minio:endpointUrl: http://192.168.136.142:9001accessKey: adminsecreKey: admin123456bucketName: sph

通过postman进行测试。

2.5 前端对接

修改sysUser.vue上传图片的页面代码,如下所示:

<el-uploadclass="avatar-uploader"action="http://localhost:8503/admin/system/fileUpload":show-file-list="false":on-success="handleAvatarSuccess":headers="headers"
><script setup>
import { useApp } from '@/pinia/modules/app'const headers = {token: useApp().authorization.token     // 从pinia中获取token,在进行文件上传的时候将token设置到请求头中
}// 图像上传成功以后的事件处理函数
const handleAvatarSuccess = (response, uploadFile) => {sysUser.value.avatar = response.data
}</script>

3 分配角色

3.1 需求分析

当用户点击"分配角色"按钮的时候,此时就会弹出一个对话框,在该对话框中会展示出来系统中所有的角色信息并且设置用户已有角色默认选中。用户此时就可以选择对应的角色,选择完毕以后,点击确定按钮,此时就需要请求后端接口,将选中的角色数据保存保存到sys_user_role表中。

效果图如下所示:

在这里插入图片描述

3.2 页面制作

具体代码如下所示:

<el-button type="warning" size="small" @click="showAssignRole(scope.row)">分配角色
</el-button><el-dialog v-model="dialogRoleVisible" title="分配角色" width="40%"><el-form label-width="80px"><el-form-item label="用户名"><el-input disabled :value="sysUser.userName"></el-input></el-form-item><el-form-item label="角色列表"><el-checkbox-group v-model="userRoleIds"><el-checkbox v-for="role in allRoles" :key="role.id" :checked="role.checked" :label="role.id">{{ role.roleName }}</el-checkbox></el-checkbox-group></el-form-item><el-form-item><el-button type="primary">提交</el-button><el-button @click="dialogRoleVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>// 角色列表
const userRoleIds = ref([])
const allRoles = ref([{"id":1 , "roleName":"管理员","checked":true},{"id":2 , "roleName":"业务人员","checked":false},{"id":3 , "roleName":"商品录入员","checked":true},
])
const dialogRoleVisible = ref(false)
const showAssignRole = async row => {sysUser.value = rowdialogRoleVisible.value = true
}</script>

3.3 查询所有角色

首先需要将系统中所有的角色数据都查询出来,在前端给用户展示出来。

3.3.1 后端接口

SysRole

添加额外属性:是否已分配角色

private Boolean checked;
SysRoleController

表现层代码实现:

// com.atguigu.spzx.manager.controller.SysRoleController
@GetMapping(value = "/findAllRoles/{userId}")
public Result findAllRoles(@PathVariable("userId")Long userId) {List<SysRole> sysRoleList =  = sysRoleService.findAllRoles(userId);return Result.ok().data(sysRoleList);
}
SysRoleService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl.SysRoleServiceImpl
@Override
public Map<String, Object> findAllRoles() {return sysRoleMapper.findSysRolesAndCheckedStatusByUserId(userId);
}
SysRoleMapper

持久层代码实现:

@Mapper
public interface SysRoleMapper {public abstract List<SysRole> findAllRoles();
}
SysRoleMapper.xml

在映射文件中添加sql语句:

<!-- 查询所有的角色数据 -->
<select id="findSysRolesAndCheckedStatusByUserId"resultType="com.atguigu.spzx.model.entity.system.SysRole">SELECT t1.* ,IF(t2.id IS NULL , FALSE , TRUE) 'checked'FROM sys_role t1LEFT JOIN sys_user_role t2ON t1.id = t2.role_idAND t2.user_id = #{userId}WHERE t1.is_deleted = 0
</select>

3.3.2 前端对接

sysRole.js

在src/api/sysRole.js文件中添加如下的请求方法:

// 查询所有的角色数据
export const GetAllRoleList = (userId) => {return request({url: `/admin/system/sysRole/findAllRoles/${userId}`,method: 'get'})
}
sysUser.vue

更改sysUser.vue的代码,添加查询所有角色数据的方法调用,如下所示:

<script setup>
import { GetAllRoleList } from '@/api/sysRole'; // 角色列表
const userRoleIds = ref([])
const allRoles = ref([{"id":1 , "roleName":"管理员"},{"id":2 , "roleName":"业务人员"},{"id":3 , "roleName":"商品录入员"},
])
const dialogRoleVisible = ref(false)
const showAssignRole = async row => {sysUser.value = rowdialogRoleVisible.value = true// 查询所有的角色数据const {code , message , data } = await GetAllRoleList(row.id) ;allRoles.value = data}
</script>

3.4 保存角色数据

3.4.1 后端接口

AssginRoleDto

创建一个实体类,封装请求参数,如下所示:

@Data
public class AssginRoleDto {private Long userId;				// 用户的idprivate List<Long> roleIdList;		// 角色id}
SysRoleUser

创建一个与数据库表向对应的实体类,如下所示:

@Data
public class SysRoleUser extends BaseEntity {private Long roleId;       // 角色idprivate Long userId;       // 用户id}
SysRoleUserController

表现层代码实现:

@RestController
@RequestMapping(value = "/admin/system/sysRoleUser")
public class SysRoleUserController {@Autowiredprivate SysRoleUserService sysRoleUserService ;@PostMapping("/doAssign")public Result doAssign(@RequestBody AssginRoleDto assginRoleDto) {sysRoleUserService.doAssign(assginRoleDto) ;return Result.build(null , ResultCodeEnum.SUCCESS) ;}}
SysRoleUserService

业务层代码实现:

@Service
public class SysRoleUserServiceImpl implements SysRoleUserService {@Autowiredprivate SysRoleUserMapper sysRoleUserMapper ;@Transactional@Overridepublic void doAssign(AssginRoleDto assginRoleDto) {// 删除之前的所有的用户所对应的角色数据sysRoleUserMapper.deleteByUserId(assginRoleDto.getUserId()) ;// 分配新的角色数据List<Long> roleIdList = assginRoleDto.getRoleIdList();if(roleIdList.size() > 0) {sysRoleUserMapper.doAssign(assginRoleDto) ;}}}
SysRoleUserMapper

持久层代码实现:

@Mapper
public interface SysRoleUserMapper {public abstract void doAssign(AssginRoleDto assginRoleDto);		// 添加关联关系public abstract void deleteByUserId(Long userId);				// 根据用户的id删除数据}
SysRoleUserMapper.xml

在SysRoleUserMapper.xml文件中添加sql语句

<?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.SysRoleUserMapper"><delete id="deleteByUserId">delete from sys_user_role sur where sur.user_id = #{userId}</delete><insert id="doAssign">insert into sys_user_role(user_id , role_id , create_time , update_time , is_deleted)values<foreach collection="roleIdList" separator="," item="roleId">( #{userId} , #{roleId} , now() , now() , 0)</foreach></insert></mapper>

3.4.2 前端对接

sysUser.js

在src/sysUser.js文件中添加如下请求接口方法:

// 给用户分配角色请求
export const DoAssignRoleToUser = (assginRoleVo) => {return request({url: "/admin/system/sysRoleUser/doAssign",method: 'post',data: assginRoleVo})
}
sysUser.vue

更改sysUser.vue的代码如下所示:

// 分配角色表单提交按钮添加doAssign事件处理函数
<el-button type="primary" @click="doAssign">提交</el-button><script setup>
import { DoAssignRoleToUser} from '@/api/sysUser'; // 角色分配按钮事件处理函数
const doAssign = async () => {let assginRoleVo = {userId: sysUser.value.id ,roleIdList: userRoleIds.value}const { code , message , data} = await DoAssignRoleToUser(assginRoleVo) ;if(code === 200) {ElMessage.success('操作成功')dialogRoleVisible.value = false fetchData()}
}
</script>
<?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.SysRoleUserMapper"><delete id="deleteByUserId">delete from sys_user_role sur where sur.user_id = #{userId}</delete><insert id="doAssign">insert into sys_user_role(user_id , role_id , create_time , update_time , is_deleted)values<foreach collection="roleIdList" separator="," item="roleId">( #{userId} , #{roleId} , now() , now() , 0)</foreach></insert></mapper>

3.4.2 前端对接

sysUser.js

在src/sysUser.js文件中添加如下请求接口方法:

// 给用户分配角色请求
export const DoAssignRoleToUser = (assginRoleVo) => {return request({url: "/admin/system/sysRoleUser/doAssign",method: 'post',data: assginRoleVo})
}
sysUser.vue

更改sysUser.vue的代码如下所示:

// 分配角色表单提交按钮添加doAssign事件处理函数
<el-button type="primary" @click="doAssign">提交</el-button><script setup>
import { DoAssignRoleToUser} from '@/api/sysUser'; // 角色分配按钮事件处理函数
const doAssign = async () => {let assginRoleVo = {userId: sysUser.value.id ,roleIdList: userRoleIds.value}const { code , message , data} = await DoAssignRoleToUser(assginRoleVo) ;if(code === 200) {ElMessage.success('操作成功')dialogRoleVisible.value = false fetchData()}
}
</script>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ky10-server docker 离线安装包、离线安装

离线安装脚本 # ---------------离线安装docker------------------- rpm -Uvh --force --nodeps *.rpm# 修改docker拉取源为国内 rm -rf /etc/docker mkdir -p /etc/docker touch /etc/docker/daemon.json cat >/etc/docker/daemon.json<<EOF{"registry-mirro…

力扣hot100题解(python版48-50题)

48、路径总和III 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从…

力扣hot100题解(python版41-43题)

41、二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例…

Unity将4个纹理图拼接成1个纹理

需要的效果 最终实现的效果大概如下: 4个贴图上去 这里随便放一个切分的图。 Shader代码如下 直接上代码: // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)// Unlit shader. Simplest possible textured shad…

DFA还原白盒AES密钥

本期内容是关于某app模拟登录的,涉及的知识点比较多,有unidbg补环境及辅助还原算法,ida中的md5以及白盒aes,fart脱壳,frida反调试 本章所有样本及资料均上传到了123云盘 llb资料官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 目录 首先抓包 fart脱壳 加密位置定位…

JavaWeb--JDBC

一&#xff1a;JDBC概述 1.概念 JDBC 就是使用Java语言操作关系型数据库的一套API 全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接 2.本质 官方&#xff08; sun 公司&#xff09;定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff1b;各个…

【C语言】熟悉文件基础知识

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 文件 为了数据持久化保存&#xff0c;使用文件&#xff0c;否则数据存储在内存中&#xff0c;程序退出&#xff0c;内存回收&#xff0c;数据就会丢失。 程序设计中&…

代码随想录算法训练营第46天| 139.单词拆分、背包问题总结

139.单词拆分 完成 思路&#xff1a; 本题可以用背包问题的思路解决&#xff0c;单词是物品&#xff0c;字符串是背包&#xff0c;要求物品能否把背包装满。 dp[j] 字符串长度为j时&#xff0c;能否拆分为一个或多个在字典中出现的单词。 递推公式为&#xff1a;if([i, j] 这个…

“平民化”非结构数据处理

在全球信息产业高速发展的背景下&#xff0c;IDC预测&#xff0c;2018 到 2025 年之间&#xff0c;全球产生的数据量将会从 33 ZB 增长到 175 ZB&#xff0c; 复合增长率27%&#xff0c;其中超过 80%的数据都会是处理难度较大的非结构化数据&#xff0c;如文档、文本、图形、图…

备战蓝桥杯---状态压缩DP基础1之棋盘问题

它只是一种手段&#xff0c;一种直观而高效地表示复杂状态的手段。 我们先来看一道比较基础的&#xff1a; 直接DFS是肯定不行&#xff0c;我们发现对某一行&#xff0c;只要它前面放的位置都一样&#xff0c;那么后面的结果也一样。 因此我们考虑用DP&#xff0c;并且只有0/…

WEB服务器-Tomcat(黑马学习笔记)

简介 服务器概述 服务器硬件 ● 指的也是计算机&#xff0c;只不过服务器要比我们日常使用的计算机大很多。 服务器&#xff0c;也称伺服器。是提供计算服务的设备。由于服务器需要响应服务请求&#xff0c;并进行处理&#xff0c;因此一般来说服务器应具备承担服务并且保障…

flutter简单的MethodChannel通道Demo(引入调用小红书sdk)

flutter端创建MethodChannel类 import package:flutter/services.dart;//MethodChannel const methodChannel const MethodChannel(com.flutter.demo.MethodChannel);class FlutterMethodChannel {/** MethodChannel flutter给原生发信息* 在方法通道上调用方法invokeMethod*…

用冒泡排序模拟C语言中的内置快排函数qsort!

目录 ​编辑 1.回调函数的介绍 2. 回调函数实现转移表 3. 冒泡排序的实现 4. qsort的介绍和使用 5. qsort的模拟实现 6. 完结散花 悟已往之不谏&#xff0c;知来者犹可追 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#xff0c;别忘了给个免…

《TCP/IP详解 卷一》第9章 广播和组播

目录 9.1 引言 9.2 广播 9.2.1 使用广播地址 9.2.2 发送广播数据报 9.3 组播 9.3.1 将组播IP地址转换为组播MAC地址 9.3.2 例子 9.3.3 发送组播数据报 9.3.4 接收组播数据报 9.3.5 主机地址过滤 9.4 IGMP协议和MLD协议 9.4.1 组成员的IGMP和MLD处理 9.4.2 组播路由…

继电器测试中需要注意的安全事项有哪些?

继电器广泛应用于电气控制系统中的开关元件&#xff0c;其主要功能是在输入信号的控制下实现输出电路的断开或闭合。在继电器测试过程中&#xff0c;为了确保测试的准确性和安全性&#xff0c;需要遵循一定的安全事项。以下是在进行继电器测试时需要注意的安全事项&#xff1a;…

汽车大灯尾灯划痕裂缝破洞破损掉角崩角等如何修复?根本没必要换车灯换总成,使用无痕修UV树脂胶液即可轻松搞定。

TADHE车灯无痕修复专用UV胶是一种经过处理的UV树脂胶&#xff0c;主要成份是改性丙烯酸UV树脂。应用在车灯的专业无痕修复领域。 车灯修复UV树脂有以下优点&#xff1a; 1. 快速修复&#xff1a;此UV树脂是一种用UV光照射在10秒内固化的材料。 2. 高强度&#xff1a;UV树脂固…

LabVIEW流量控制系统

LabVIEW流量控制系统 为响应水下航行体操纵舵翼环量控制技术的试验研究需求&#xff0c;通过LabVIEW开发了一套小量程流量控制系统。该系统能够满足特定流量控制范围及精度要求&#xff0c;展现了其在实验研究中的经济性、可靠性和实用性&#xff0c;具有良好的推广价值。 项…

抖音视频批量下载软件|视频评论采集工具

抖音视频评论采集软件是一款基于C#开发的高效、便捷的工具&#xff0c;旨在为用户提供全面的数据采集和分析服务。用户可以通过关键词搜索抓取视频数据&#xff0c;也可以通过分享链接进行单个视频的抓取和下载&#xff0c;从而轻松获取抖音视频评论数据。 批量视频提取模块&a…

数学建模函数插值与拟合

1.脑图 2.介绍 我们自己找到的函数&#xff0c;在已知点处的函数值和要求的函数在这些点处的函数值相等&#xff0c;这个函数 就叫做未知函数的插值函数&#xff1b; 多项式函数构成的插值函数的集合叫做函数类&#xff1b; 3.拉格朗日插值法 基函数的求法和插值函数的构造…

Java SPI机制详解

Java SPI机制详解 1. 定义接口2. 实现接口4. 创建配置文件5. 加载实现类6.Java SPI机制在MySQL中的使用 总结 SPI 全称为 (Service Provider Interface) &#xff0c;是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制&#xff0c; 当我们有个接口&#xff0c;想在…