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.mzph.cn/news/710448.shtml

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

相关文章

ChatGPT-4 AI 绘图魔力释放

最近刚开通了 ChatGPT4&#xff0c;正好要设计一个网站图标&#xff0c;想测试一下它AI绘图的能力&#xff0c;让它根据文字描述生成一个想象中的图标 &#xff08;PS&#xff1a;如果想体验 GPT4 文生图&#xff0c;可以看这个教程 如何升级 ChatGPT 4.0&#xff09; 第1次交…

【三维重建】【SLAM】SplaTAM:基于3D高斯的密集RGB-D SLAM

题目&#xff1a;SplaTAM: Splat, Track & Map 3D Gaussians for Dense RGB-D SLAM 地址&#xff1a;spla-tam.github.io 机构&#xff1a;CMU&#xff08;卡内基梅隆大学&#xff09;、MIT&#xff08;美国麻省理工&#xff09; 总结&#xff1a;SplaTAM&#xff0c;一个新…

ywtool network命令

一.network功能介绍 network功能就是通过脚本的方式配置IP信息&#xff0c;分为4项: (1) 配置单网卡(2)配置br网桥(单网卡)(3)配置bond(两张网卡)(4)配置ovs网桥(单网卡) 日志文件:/var/log/ywtools/ywtools-network.log/usr/local/ywtools/config/config.ini中network参数:…

从预训练到通用智能(AGI)的观察和思考

1.预训练词向量 预训练词向量&#xff08;Pre-trained Word Embeddings&#xff09;是指通过无监督学习方法预先训练好的词与向量之间的映射关系。这些向量通常具有高维稠密特征&#xff0c;能够捕捉词语间的语义和语法相似性。最著名的预训练词向量包括Google的Word2Vec&#…

项目实现json字段

有些很复杂的信息&#xff0c;我们一般会用扩展字段传一个json串&#xff0c;字段一般用text类型存在数据库。mysql5.7以后支持json类型的字段&#xff0c;还可以进行sql查询与修改json内的某个字段的能力。 1.json字段定义 ip_info json DEFAULT NULL COMMENT ip信息, 2.按…

应用存储与持久化数据卷

1、PV 引入场景&#xff1a; ① Deployment 管理的 pod&#xff0c;在做镜像升级的过程中&#xff0c;会产生新的 pod并且删除旧的 pod &#xff0c;新旧 pod 之间如何复用数据&#xff1f; ② 宿主机宕机的时候&#xff0c;如何实现带卷迁移&#xff1f; ③ 多个 pod 之间&…

Redis 之三:发布订阅(pub/sub)

概念介绍 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff0c;它允许客户端之间进行异步的消息传递 Redis 客户端可以订阅任意数量的频道。 模型中的角色 在该模型中&#xff0c;有三种角色&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;负责发送信…

[分类指标]准确率、精确率、召回率、F1值、ROC和AUC、MCC马修相关系数

准确率、精确率、召回率、F1值 定义&#xff1a; 1、准确率&#xff08;Accuracy&#xff09; 准确率是指分类正确的样本占总样本个数的比例。准确率是针对所有样本的统计量。它被定义为&#xff1a; 准确率能够清晰的判断我们模型的表现&#xff0c;但有一个严重的缺陷&…

在Windows系统中启动Redis服务

前言 Redis是一个开源、高性能的键值对数据库&#xff0c;常用于缓存、消息队列等场景。本文将详细指导您如何在Windows系统上启动Redis服务。 第一步&#xff1a;确认Redis安装 确保您已经在Windows系统上成功安装了Redis。官方提供了预编译好的Windows版本&#xff0c;您可…

低代码中的可视化表单:效率与灵活兼备的设计工具

近年来&#xff0c;随着数字化转型的加速推进&#xff0c;企业对于高效率、灵活性和可定制性的软件开发需求不断增长。传统的软件开发过程通常需要耗费大量的时间和资源&#xff0c;而低代码开发平台的出现为企业提供了一种更加快速和灵活的解决方案。在低代码开发平台中&#…

Linux centos 变更MySQL数据存储路径

Linux centos 变更MySQL数据存储路径 登录mysql&#xff0c;查看数据存储路径创建新目录准备迁移数据检查是否配置成功 登录mysql&#xff0c;查看数据存储路径 mysql -u root -pshow global variables like "%datadir%";创建新目录 查看磁盘空间 df -h选取最大磁…

不可多得的干货,网易的朋友给我这份339页的Android面经

这里先放上目录 一 性能优化 1.如何对 Android 应用进行性能分析 android 性能主要之响应速度 和UI刷新速度。 首先从函数的耗时来说&#xff0c;有一个工具TraceView 这是androidsdk自带的工作&#xff0c;用于测量函数耗时的。 UI布局的分析&#xff0c;可以有2块&#x…

本届挑战赛亚军方案:基于大模型和多AGENT协同的运维

“轻舟已过万重山团队”荣获本届挑战赛亚军&#xff0c;该团队来自华为集团IT-UniAI 产品和openEuler系统智能团队。 方案介绍 自ChatGPT问世以来&#xff0c;AI迎来了奇点iPhone时刻&#xff0c;这一年来大模型深入影响企业办公&#xff0c;金融&#xff0c;广告&#xff0c;…

打造去中心化透明储蓄罐:Solidity智能合约的又一实践

一、案例背景 传统的储蓄罐通常是由个人或家庭使用&#xff0c;用于存放硬币或小额纸币。然而&#xff0c;这样的储蓄罐缺乏透明性&#xff0c;用户无法实时了解储蓄情况&#xff0c;也无法确保资金的安全性。 通过Solidity智能合约&#xff0c;我们可以构建一个去中心化…

转前端了!!

大家好&#xff0c;我是冰河~~ 没错&#xff0c;为了更好的设计和开发分布式IM即时通讯系统&#xff0c;也为了让大家能够直观的体验到分布式IM即时通讯系统的功能&#xff0c;冰河开始转战前端了。也就是说&#xff0c;整个项目从需求立项到产品设计&#xff0c;从架构设计到…

leetcode 热题 100_字母异位词分组

题解一&#xff1a; 排序&#xff1a;对两个字母异位词&#xff0c;二者排序后的字符串完全一样&#xff0c;因此可以对所给字符串进行排序&#xff0c;以排序后的字符串作为HashMap哈希表的键值&#xff0c;将排序前的字符串作为值进行存储分组&#xff0c;最后返回。 import…

Opencv基础与学习路线

Opencv Opencv每一篇目具体&#xff1a; Opencv(1)读取与图像操作 Opencv(2)绘图与图像操作 Opencv(3)详解霍夫变换 Opencv(4)详解轮廓 Opencv(5)平滑处理 具体Opencv相关demo代码欢迎访问我的github仓库&#xff08;包含python和c代码&#xff09; demo代码 文章目录 Opencv一…

3d图形学基础(一):向量与坐标系

文章目录 1.1 向量与坐标系1.1.1 向量与坐标系的应用1.1.2 完整测试代码 1.1 向量与坐标系 1.1.1 向量与坐标系的应用 零向量&#xff1a; 零向量是没有方向的向量&#xff1b; 负向量&#xff1a; 负向量是与原向量方向相反、长度相等的向量&#xff1b; 向量的模&#xf…

学不动系列-git-hooks和husky+lintstage

git-hooks 为了保证提交的代码符合规范&#xff0c;可以在上传代码时进行校验。常用husky来协助进行代码提交时的eslint校验。husky是基于git-hooks来实现&#xff0c;在使用husky之前&#xff0c;我们先来研究一下git-hooks。 构建git-hooks测试项目 需要使用git-hooks就需…

QPaint绘制自定义仪表盘组件03

网上视频抄的&#xff0c;用来自己看一下&#xff0c;看完就删掉 ui mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QDebug> #include <QtMath> #include <QDialog> #include <QPainter> #include …