别迷路,先收藏!!!
关注我公众号,汪程序员
https://www.bilibili.com/video/BV1XT411u7Dk/
回复“熊猫电影sql”
https://github.com/WangWenShuai529/FilmSystem
给个start!!!
代码也可以通过公众号回复“熊猫电影代码”。
https://download.csdn.net/download/m0_61504367/85913163
人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站
https://www.captainai.net/shuai
main.js
import Vue from 'vue'
import './plugins/axios'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
// 导入字体图标
import './assets/css/fonts/iconfont.css'
import {Message} from 'element-ui'
import has from './assets/js/permission'
import global_variable from "@/components/global_variable";
// import echarts from 'echarts'
// Vue.prototype.$echarts = echarts
Vue.prototype.global = global_variable
//默认的跨域url,后端的端口号为8181
axios.defaults.baseURL = 'http://127.0.0.1:8181/'
axios.interceptors.request.use(config => {
config.headers.Token = window.sessionStorage.getItem('token')
return config
})
// 状态码错误信息
const codeMessage = {
401: '用户没有权限,请登录',
403: '用户没有权限,请联系管理员',
404: '访问的资源不存在',
406: '请求的格式不可得',
410: '请求的资源被永久删除,且不会再得到的',
422: '当创建一个对象时,发生一个验证错误',
500: '服务器发生错误,请检查服务器',
502: '网关错误',
503: '服务不可用,服务器暂时过载或维护',
504: '网关超时'
};
axios.interceptors.response.use(response => {
return response
}, error => {
if(error){
if(!error.response){
return console.log('Error', error.message);
}
//获取状态码
const status = error.response.status;
const errortext = codeMessage[status] || error.response.statusText;
//提示错误信息
Message.error(errortext)
// 错误状态处理
if (status === 401) {
router.push('/login')
} else if (status === 403) {
router.push('/login')
} else if (status >= 404 && status < 422) {
router.push('/404')
}
}
return Promise.reject(error)
})
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
后端也需要接收跨域的请求,这里在后面实现
<template>
<div class="login_container">
<div class="login_box">
<div class="title_box">
<p>熊猫影院管理登录</p>
</div>
<!-- 登录表单区域 -->
<!-- loginForm双向绑定 -->
<el-form class="login_form" :model="loginForm" :rules="loginFormRules" ref="loginFormRef">
<!-- 用户名 -->
<el-form-item prop="userName">
<el-input v-model="loginForm.userName" placeholder="请输入用户名" clearable
prefix-icon="iconfont icon-user"></el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password
prefix-icon="iconfont icon-lock"></el-input>
</el-form-item>
<!-- 按扭区域 -->
<el-form-item class="btns">
<el-button size="medium" :round="true" type="primary" @click="login">点击登录</el-button>
<el-button size="medium" :round="true" type="info" @click="resetLoginForm">恢复默认</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
//登录表单数据对象
//设立初始值,方便调试
loginForm: {
userName: 'admin',
password: '123456'
},
//表单验证规则
loginFormRules: {
//验证用户名
userName: [
{ required: true, message: "请输入用户名称", trigger: "blur"},
{ min:2, max: 20, message: "长度在2到20个字符之间", trigger: "blur"}
],
//验证密码
password: [
{ required: true, message: "请输入密码", trigger: "blur"},
{ min:6, max: 16, message: "长度在6到16个字符之间", trigger: "blur"}
]
}
}
},
methods:{
success(params) {
console.log(params);
this.login()
},
//点击重置按钮,重置表单
resetLoginForm(){
console.log(this.$refs)
//只是将查询条件初始化,所以在初始化时绑定什么值就还是什么值。用于清空表单
this.$refs.loginFormRef.resetFields();
},
login() {
this.$refs.loginFormRef.validate(async valid => {
//用于表单的验证
if(!valid) return;
axios.defaults.headers.post['Content-Type'] = 'application/json'
//JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串。
const { data: res} = await axios.post('sysUser/login', JSON.stringify(this.loginForm));
//登录失败
if(res.code !== 200) return this.$message.error(res.msg);
//控制登录权限
if(res.data.sysUser.sysRole.children === null || res.data.sysUser.sysRole.children[0] === null) {
this.$message.error("抱歉,您没有权限登录,请联系管理员获取权限")
return
}
this.$message.success("登录成功")
// console.log(res.data);
//保存token,放入sessionStorage,浏览器关闭就会取消
window.sessionStorage.setItem("token", res.data.token)
window.sessionStorage.setItem("loginUser", JSON.stringify({sysUser : res.data.sysUser, cinemaId : res.data.cinemaId, cinemaName : res.data.cinemaName}));
// window.sessionStorage.setItem("btnPermission", res.data.sysUser.sysRole.roleId === 1 ? "admin" : "normal")
window.sessionStorage.setItem("btnPermission", res.data.sysUser.sysRole.roleId === 1 ? "admin" : "admin")
//导航跳转到首页
await this.$router.push('/welcome');
})
}
}
}
</script>
<style scoped>
.login_container{
/*背景图片*/
background-image: url("../assets/login-background1.jpg");
height: 100%;
}
.login_box{
width: 450px;
height: 300px;
background-color: #fff;
border-radius: 3px;
position: absolute;
left: 50%;
top: 50%;
/*一般用于居中,这个其实就是一个位移的属性,translatex在x轴方向上进行移动,反之translatey实在y轴方向,而translate括号里的两个参数是先x后y的。*/
transform: translate(-50%, -50%);
}
.avatar_box{
height: 130px;
width: 130px;
border: 1px solid #eee;
border-radius: 50%;
padding: 10px;
box-shadow: 0 0 10px #ddd;
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
}
.avatar_box > img{
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #eee;
}
.title_box{
text-align: center;
font-size: 200%;
}
.login_form{
position: absolute;
bottom: 0;
width: 100%;
padding: 0 20px;
box-sizing: border-box;
}
.btns{
display: flex;
justify-content: center;
}
</style>
<template>
<div>
<!--面包屑导航区域-->
<div class="board">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/welcome' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>熊猫影院管理</el-breadcrumb-item>
<el-breadcrumb-item>熊猫影院信息管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!--卡片视图-->
<el-card class="box-card">
<!--表格显示影院信息-->
<el-form :model="cinemaInfo" label-width="150px">
<el-form-item label="公司名称: " prop="cinemaName">
<!-- 显示cinemaInfo的信息-->
<el-input class="el-input-show" v-model="cinemaInfo.cinemaName" disabled></el-input>
</el-form-item>
<el-form-item label="公司地址: " prop="cinemaAddress">
<el-input class="el-input-show" v-model="cinemaInfo.cinemaAddress" disabled></el-input>
</el-form-item>
<el-form-item label="营业电话: " prop="cinemaPhone">
<el-input class="el-input-show" v-model="cinemaInfo.cinemaPhone" disabled></el-input>
</el-form-item>
<el-form-item label="营业时间: " prop="cinemaPhone">
<el-input class="el-input-show-time" v-model="cinemaInfo.workStartTime" disabled></el-input>
至
<el-input class="el-input-show-time" v-model="cinemaInfo.workEndTime" disabled></el-input>
</el-form-item>
<el-form-item label="拥有电影类型: " prop="hallCategory">
<el-tag v-for="hall in halls" >{{hall}}</el-tag>
</el-form-item>
<el-form-item label="影院图片: ">
<span v-for="item in pics">
<el-popover placement="left" trigger="click" width="300">
<img :src="item.url" width="200%"/>
<!-- 图片后端的地址:["/images/cinema/2020/12/15/123.jpg"]-->
<img slot="reference" :src="item.url" :alt="item"
style="max-height: 300px;max-width: 300px;padding: 10px"/>
</el-popover>
</span>
</el-form-item>
<el-form-item label="" prop="cinemaName">
<el-button type="primary" @click="showEditDialog">修改影院信息</el-button>
</el-form-item>
</el-form>
</el-card>
<!--修改影院对话框-->
<el-dialog title="修改影院" :visible.sync="editDialogVisible" width="60%" @close="editDialogClosed">
<!-- 绑定editForm-->
<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="150px">
<el-form-item label="影院名称" prop="cinemaName">
<el-input v-model="editForm.cinemaName"></el-input>
</el-form-item>
<el-form-item label="影院地址" prop="cinemaAddress">
<el-input v-model="editForm.cinemaAddress"></el-input>
</el-form-item>
<el-form-item label="影院电话" prop="cinemaPhone">
<el-input v-model="editForm.cinemaPhone"></el-input>
</el-form-item>
<el-form-item label="开始营业时间" prop="workStartTime">
<el-time-picker
v-model="editForm.workStartTime"
value-format="HH:mm"
placeholder="选择开始营业时间">
</el-time-picker>
</el-form-item>
<el-form-item label="结束营业时间" prop="workEndTime">
<el-time-picker
v-model="editForm.workEndTime"
value-format="HH:mm"
placeholder="选择结束营业时间">
</el-time-picker>
</el-form-item>
<el-form-item label="拥有影厅类型" prop="hallCategoryList">
<el-input class="el-input-hall" placeholder="请输入添加影厅类别名称" v-model="inputHall" clearable></el-input>
<el-button type="primary" @click="addHallCategory()">添加</el-button>
</el-form-item>
<el-form-item >
<el-tag
v-for="(item, index) in halls"
:key="index"
closable
@close="deleteHallCategory(index)">
{{item}}
</el-tag>
</el-form-item>
<el-form-item label="影院图片">
<el-upload action="" list-type="picture-card"
:auto-upload="false"
:file-list="pics"
:on-change="handleChange"
:on-success="handleSuccess"
:on-error="handleError"
ref="pictureEditRef"
:http-request="submitFile">
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{file}">
<img class="el-upload-list__item-thumbnail"
:src="file.url" alt="">
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)">
<i class="el-icon-zoom-in"></i>
</span>
<span class="el-upload-list__item-delete"
@click="handleRemove(file)">
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
<!--放大预览-->
<el-dialog :visible.sync="dialogVisible" append-to-body>
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="cancelEdit">取 消</el-button>
<el-button type="primary" @click="editCinemaInfo">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import global from "../../assets/css/global.css"
export default {
data() {
return {
//控制对话框的显示与隐藏(这个是修改的窗口)
editDialogVisible: false,
editForm: {},
cinemaInfo: {},
//校验规则
editFormRules: {
cinemaName: [
{ required: true, message: '请输入影院名称', trigger: 'change' }
],
cinemaAddress: [
{ required: true, message: '请输入影院地址', trigger: 'change' }
],
cinemaPhone: [
{ required: true, message: '请输入影院电话', trigger: 'change' }
]
},
inputHall: '',
dialogImageUrl: '',
dialogVisible: false,
hideUpload: false,
//添加删除图片 动态绑定图片列表
pics: [],
//添加删除影厅类别 动态绑定影厅列表
halls: [],
// 发送给后端的JSON图片数组
pictureList: [],
picNums: 0,
deletePicList: []
}
},
created() {
this.getCinemaInfo()
},
methods: {
//请求数据
async getCinemaInfo() {
const _this = this
await axios.get('sysCinema').then(resp => {
_this.cinemaInfo = resp.data.data
})
_this.pics = []
_this.halls = []
//解开json数组,使用 JSON.parse() 方法将数据转换为 JavaScript 对象。
for (const item of JSON.parse(this.cinemaInfo.cinemaPicture)) {
let pic = {}
pic['name'] = ''
pic['url'] = this.global.base + item
this.pics.push(pic)
}
for (const item of JSON.parse(this.cinemaInfo.hallCategoryList)) {
this.halls.push(item)
}
},
// 显示修改对话框,回显数据
async showEditDialog() {
//修改表单的赋值
this.editForm = this.cinemaInfo
//把false改成true,显示
this.editDialogVisible = true
},
// 监听修改对话框的关闭事件
editDialogClosed() {
this.$refs.editFormRef.resetFields()
this.$refs.pictureEditRef.clearFiles()
this.pics = []
this.pictureList = []
this.halls = []
//重新获得界面的数据
this.getCinemaInfo()
},
// 取消修改
cancelEdit() {
this.editDialogVisible = false
//把删除的数据移除列表,splice(index) ——> 从index的位置开始,删除之后的所有元素(包括第index个)
this.deletePicList.splice(0, this.deletePicList.length)
},
//异步请求,修改cinemaInfo
async editCinemaInfo() {
//提交图片
await this.submitFile()
//JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串。
this.editForm.cinemaPicture = JSON.stringify(this.pictureList)
this.editForm.hallCategoryList = JSON.stringify(this.halls)
this.$refs.editFormRef.validate(async valid => {
const _this = this
if (!valid) return
let success = true
axios.defaults.headers.put['Content-Type'] = 'application/json'
//async 关键字用于函数上(async函数的返回值是Promise实例对象)
// await 关键字用于 async 函数当中(await可以得到异步的结果)
await axios.put('sysCinema/update', JSON.stringify(_this.editForm)).then(resp => {
if (resp.data.code !== 200){
this.$message.error('修改影院信息失败!')
success = false
}
})
if (!success) return
this.editDialogVisible = false
await this.getCinemaInfo()
this.$message.success('修改影院信息成功!')
//删除数据库中的删除图片
for(let item of this.deletePicList) {
//substring() 方法用于提取字符串中介于两个指定下标之间的字符。
//indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
await axios.get('/upload/delete?filePath=' + item.substring(item.indexOf('/images')))
}
})
},
//增加类型
addHallCategory() {
if (this.inputHall === '' || this.inputHall === null) {
this.$alert('影厅类别添加失败!原因:所添加的影厅类别不能为空。', '影厅类别添加异常', {
confirmButtonText: '我知道了'
})
return
} else if (!this.halls.includes(this.inputHall)) {
this.halls.push(this.inputHall)
} else {
console.log('已存在')
this.$alert('影厅类别添加失败!原因:所添加的影厅类别已存在。', '影厅类别添加异常', {
confirmButtonText: '我知道了'
})
}
this.inputHall = ''
},
//删除类型
deleteHallCategory(index) {
this.halls.splice(index, 1)
console.log(this.halls)
},
//删除图片
handleRemove(file, fileList) {
const filePath = file.url
console.log(filePath)
const idx = this.pics.findIndex(x => x.url === filePath)
if (file.status === 'success') {
this.deletePicList.push(file.url)
}
this.pics.splice(idx, 1)
},
//展示图片
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
handleChange(file, filelist){
//slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
this.pics = filelist.slice(0)
},
handleSuccess(response){
this.pictureList.push(response.data)
this.editForm = JSON.stringify(this.pictureList)
},
handleError(err){
console.log(err)
},
//提交文件
async submitFile() {
const _this = this
for (let i = 0; i < this.pics.length; i++){
let formData = new FormData()
if (this.pics[i].status === 'success') {
let s = this.pics[i].url
//加入到pictureList
this.pictureList.push(s.substring(s.indexOf('/images')))
// 显示: /images/cinema/2020/12/15/123.jpg
// console.log(s.substring(s.indexOf('/images')));
continue
}
let file = this.pics[i].raw
formData.append('file', file)
await axios.post('upload/cinema', formData).then(response =>{
_this.pictureList.push(response.data.data)
})
}
}
}
}
</script>
<style scoped>
.el-tag{
margin: 0 10px 10px 0;
}
.row{
white-space: nowrap;
margin-top: 10px;
padding: 0 10px;
text-align: center;
display: flex;
justify-content: space-between;
}
.row2{
margin-top: 20px;
}
.el-input-show{
width: 420px;
}
.el-input-show-time{
width: 100px;
}
.el-input-hall {
width: 240px;
margin: 0 20px 0px 0;
}
</style>
选择时间,利用的DateTimePicker 日期时间选择器
注意:添加、修改电影信息对话框,代码大同小异。
一个绑定的是addForm,另外一个是editForm。
<template>
<div>
<!--面包屑导航类别-->
<div class="board">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/welcome' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>电影管理</el-breadcrumb-item>
<el-breadcrumb-item>电影类别</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!--卡片视图-->
<el-card class="box-card">
<el-row :gutter="20">
<el-col :span="2">
<el-button type="primary" @click="addDialogVisible = true">添加类别</el-button>
</el-col>
<el-col :span="2">
<el-button type="danger" @click="multipleDelete">批量删除类别</el-button>
</el-col>
</el-row>
<!--类别分类列表-->
<el-table :data="movieCategoryList" style="width: 45%" border stripe @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="movieCategoryId" label="类别编号" width="145"></el-table-column>
<el-table-column prop="movieCategoryName" label="电影类别" width="180"></el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-tooltip effect="dark" content="修改电影类别" placement="top" :enterable="false" :open-delay="500">
<!-- showEditDialog(scope.row.movieCategoryId),这一行的ID值 -->
<el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.movieCategoryId)"></el-button>
</el-tooltip>
<el-tooltip effect="dark" content="删除类别" placement="top" :enterable="false" :open-delay="500">
<el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteMovieCategoryById(scope.row.movieCategoryId)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<!--分页类别-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pageNum"
:page-sizes="[5, 7, 9]"
:page-size="queryInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!--添加类别对话框-->
<el-dialog title="添加类别" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed">
<!--内容主题区-->
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px">
<!--prop:在addFormRules中定义校验规则, v-model:双向绑定数据-->
<el-form-item label="电影类别" prop="movieCategoryName">
<el-input v-model="addForm.movieCategoryName"></el-input>
</el-form-item>
</el-form>
<!--底部区域-->
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addMovieCategory">确 定</el-button>
</span>
</el-dialog>
<!--修改类别对话框-->
<el-dialog title="修改类别" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed">
<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px">
<el-form-item label="类别编号">
<el-input v-model="editForm.movieCategoryId" disabled></el-input>
</el-form-item>
<el-form-item label="电影类别" prop="movieCategoryName">
<el-input v-model="editForm.movieCategoryName"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="editMovieCategoryInfo">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "MovieCategory",
// this.$message和this.$confirm都属于原型挂载, 在element.js中配置
// Vue.prototype.$message = Message
// Vue.prototype.$confirm = MessageBox.confirm
data() {
return {
queryInfo: {
query: '',
pageNum: 1,
pageSize: 7
},
movieCategoryList: [],
total: 0,
//控制对话框的显示与隐藏
addDialogVisible: false,
//添加类别的表单数据
addForm: {
movieCategoryName: ''
},
//验证表单规则对象
addFormRules: {
movieCategoryName: [
{ required: true, message: '请输入电影类别', trigger: 'blur' }
]
},
editDialogVisible: false,
editForm: {},
editFormRules: {
movieCategoryName: [
{ required: true, message: '请输入电影类别', trigger: 'blur' }
]
},
multipleSelection: []
}
},
created() {
this.getMovieCategoryList()
},
methods: {
getMovieCategoryList() {
const _this = this;
axios.get('sysMovieCategory/find', {params: _this.queryInfo}).then(resp => {
console.log(resp)
_this.movieCategoryList = resp.data.data;
_this.total = resp.data.total;
_this.queryInfo.pageSize = resp.data.pageSize;
_this.queryInfo.pageNum = resp.data.pageNum;
})
},
handleSizeChange(newSize) {
this.queryInfo.pageSize = newSize
this.getMovieCategoryList()
console.log(newSize)
},
handleCurrentChange(newPage) {
this.queryInfo.pageNum = newPage
this.getMovieCategoryList()
console.log(newPage)
},
// 监听添加对话框的关闭事件
addDialogClosed(){
this.$refs.addFormRef.resetFields()
},
// 监听添加按钮
addMovieCategory(){
const _this = this;
this.$refs.addFormRef.validate(async valid => {
console.log(valid)
if (!valid) return
//预校验成功,发网络请求
axios.defaults.headers.post['Content-Type'] = 'application/json'
await axios.post('sysMovieCategory', JSON.stringify(_this.addForm)).then(resp => {
console.log(resp)
if (resp.data.code !== 200){
this.$message.error('添加电影类别失败!')
}
})
//隐藏添加对话框
this.addDialogVisible = false
//重新加载列表
this.getMovieCategoryList()
this.$message.success('添加电影类别成功!')
})
},
// 显示修改对话框,回显数据
showEditDialog(id){
const _this = this
axios.get('sysMovieCategory/' + id ).then(resp => {
console.log(resp)
_this.editForm = resp.data.data
})
this.editDialogVisible = true
},
// 监听修改对话框的关闭事件
editDialogClosed(){
this.$refs.editFormRef.resetFields()
},
// 修改类别分类信息并提交
editMovieCategoryInfo(){
this.$refs.editFormRef.validate(async valid => {
const _this = this
if (!valid) return
axios.defaults.headers.put['Content-Type'] = 'application/json'
await axios.put('sysMovieCategory', JSON.stringify(_this.editForm)).then(resp => {
if (resp.data.code !== 200){
this.$message.error('修改电影类别失败!')
}
})
this.editDialogVisible = false
this.getMovieCategoryList()
this.$message.success('修改电影类别成功!')
})
},
// 监听多选框变化
handleSelectionChange(val){
this.multipleSelection = val
},
async multipleDelete(){
const _this = this
//询问用户是否确认删除
const resp = await this.$confirm('此操作将永久删除这些条目, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(err => err)
// 用户确认删除, resp为字符串"confirm"
// 用户取消删除,resp为字符串"cancel"
if (resp === 'cancel'){
return _this.$message.info('已取消删除')
}
let ids = []
this.multipleSelection.forEach(data => {
ids.push(data.movieCategoryId)
})
await axios.delete('sysMovieCategory/' + ids).then(resp => {
if (resp.data.code !== 200){
this.$message.success('批量删除电影类别失败!')
}
})
this.getMovieCategoryList()
this.$message.success('批量删除电影类别成功!')
},
//根据id删除对应的类别分类
async deleteMovieCategoryById(id){
const _this = this
//询问用户是否确认删除
const resp = await this.$confirm('此操作将永久删除该条目, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(err => err)
// 用户确认删除, resp为字符串"confirm"
// 用户取消删除,resp为字符串"cancel"
console.log(resp)
if (resp === 'cancel'){
return _this.$message.info('已取消删除')
}
await axios.delete('sysMovieCategory/' + id).then(resp => {
if (resp.data.code !== 200){
_this.$message.success('删除电影类别失败!')
}
})
this.getMovieCategoryList()
this.$message.success('删除电影类别成功!')
}
}
}
</script>
<style scoped>
</style>
package com.panda.framework.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrosConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
package com.panda.framework.exception;
import com.panda.common.constant.HttpStatus;
import com.panda.common.exception.DataNotFoundException;
import com.panda.common.response.ResponseResult;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 配置异常处理器,管理全局异常
*/
@RestController
@ControllerAdvice
public class GlobalExceptionHandler {
// Logger控制台显示错误信息
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理验证不通过异常,将错误信息响应给前端
*
* @return 错误响应信息
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
//打印日志
log.error(e.getMessage(), e);
//获取该异常的结果
BindingResult bindingResult = e.getBindingResult();
//获取错误msg
String msg = bindingResult.getAllErrors().get(0).getDefaultMessage();
return ResponseResult.error(msg);
}
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public ResponseResult sqlIntegrityConstraintViolationExceptionHandler(SQLIntegrityConstraintViolationException e) {
log.error(e.getMessage(), e);
return ResponseResult.error("插入或修改操作不合法");
}
@ExceptionHandler(DataNotFoundException.class)
public ResponseResult dataNotFoundExceptionHandler(DataNotFoundException e) {
log.warn(e.getMessage());
return ResponseResult.error(e.getMessage());
}
@ExceptionHandler(NoSuchMethodException.class)
public ResponseResult noSuchMethodExceptionHandler(NoSuchMethodException e) {
log.warn(e.getMessage());
return ResponseResult.error("抱歉,服务器内部出现了些问题");
}
@ExceptionHandler(IllegalAccessException.class)
public ResponseResult illegalAccessExceptionHandler(IllegalAccessException e) {
log.warn(e.getMessage());
return ResponseResult.error("抱歉,服务器内部出现了些问题");
}
@ExceptionHandler(IOException.class)
public ResponseResult IOExceptionHandler(IOException e) {
log.warn(e.getMessage());
return ResponseResult.error("文件信息错误,原因:" + e.getMessage());
}
@ExceptionHandler(AuthenticationException.class)
public ResponseResult authenticationExceptionHandler(AuthenticationException e) {
log.warn(e.getMessage());
return ResponseResult.error(HttpStatus.BAD_REQUEST, e.getMessage());
}
}
如果对jwt不了解请先看:
JWT网络令牌原理和springboot实现
org.apache.shiro
shiro-spring-boot-starter
com.auth0
java-jwt
package com.panda.framework.config;
import com.panda.framework.shiro.JwtFilter;
import com.panda.framework.shiro.realms.CustomerRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 配置shiro安全框架
*/
@Configuration
public class ShiroConfig {
// 创建shiroFilter
@Bean("shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 给shiroFilter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 配置受限资源
Map<String, String> map = new LinkedHashMap<>();
// 放行注册和登录
map.put("/sysUser/register", "anon");
map.put("/sysUser/login", "anon");
// 放行图片查询
map.put("/images/**", "anon");
// 放行影院查询请求
map.put("/sysCinema/**", "anon");
// 放行电影查找相关请求
map.put("/sysMovie/find/**", "anon");
// 放行电影类别查找相关请求
map.put("/sysMovieCategory/find/**", "anon");
// 放行电影场次查找相关请求
map.put("/sysSession/find/**", "anon");
// 请求这个资源需要认证与授权
map.put("/sysCinema/update", "jwt");
// 请求这个资源需要认证与授权
map.put("/**", "jwt");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
filterMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);
//设置认证规则
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置realm
defaultWebSecurityManager.setRealm(realm);
//关闭shiro自带的session,使得不保存登录状态,每次使用token进行验证
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
defaultWebSecurityManager.setSubjectDAO(subjectDAO);
return defaultWebSecurityManager;
}
//创建自定义realm
@Bean(name = "realm")
public Realm getRealm() {
CustomerRealm realm = new CustomerRealm();
return realm;
}
}
自定义realm:
package com.panda.framework.shiro.realms;
import com.panda.common.utils.JwtUtil;
import com.panda.framework.shiro.JwtToken;
import com.panda.system.domin.SysUser;
import com.panda.system.service.impl.SysUserServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 自定义realm
*/
@Slf4j
public class CustomerRealm extends AuthorizingRealm {
@Autowired
private SysUserServiceImpl sysUserService;
/**
* 重写此方法避免shiro报错
*
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getCredentials();
// 解密获得username,用于和数据库进行对比
String username = null;
try {
//这里工具类没有处理空指针等异常这里处理一下(这里处理科学一些)
username = JwtUtil.getUsername(token);
} catch (Exception e) {
throw new AuthenticationException("token拼写错误或者值为空");
}
if (username == null) {
log.error("token无效(空''或者null都不行!)");
throw new AuthenticationException("token无效");
}
SysUser user = sysUserService.findUserByName(username);
if (user == null) {
log.error("用户不存在)");
throw new AuthenticationException("用户不存在");
}
if (!JwtUtil.verify(token, username, user.getPassword())) {
log.error("用户名或密码错误(token无效或者与登录者不匹配)");
throw new AuthenticationException("用户名或密码错误(token无效或者与登录者不匹配)");
}
return new SimpleAuthenticationInfo(token, token, this.getName());
}
}
使用jwt过滤器作为shiro的过滤器:
package com.panda.framework.shiro;
import com.panda.common.constant.HttpStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 使用jwt过滤器作为shiro的过滤器
*/
@Slf4j
@Component
public class JwtFilter extends BasicHttpAuthenticationFilter implements Filter {
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 把token从header中取出来
String token = httpServletRequest.getHeader("Token");
JwtToken jwtToken = new JwtToken(token);
//交给realm进行登入
getSubject(request, response).login(jwtToken);
return true;
}
/**
* 登录认证
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
return executeLogin(request, response);
} catch (Exception e) {
log.error("JwtFilter过滤验证失败");
return false;
}
}
/**
* 处理跨域
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求, 直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.SUCCESS);
return false;
}
return super.preHandle(request, response);
}
}
抽取重复功能为基类
package com.panda.web.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.panda.common.page.Page;
import com.panda.common.page.PageBuilder;
import com.panda.common.response.ResponseResult;
import java.util.List;
import static com.panda.common.page.PageBuilder.*;
/**
* 抽取重复功能为基类
*/
public class BaseController {
/**
* 开启分页
*/
public void startPage() {
Page page = PageBuilder.buildPage();
Integer pageNum = page.getPageNum();
Integer pageSize = page.getPageSize();
if (pageNum != null && pageSize != null) {
PageHelper.startPage(pageNum, pageSize, page.getOrderByColumn());
}
}
/**
* 根据修改行数返回响应消息
* @param rows
* @return
*/
public ResponseResult getResult(int rows) {
return rows == 0 ? ResponseResult.error() : ResponseResult.success();
}
/**
* 分页响应消息
* @param data
* @return
*/
public ResponseResult getResult(List<?> data) {
PageInfo pageInfo = new PageInfo(data);
ResponseResult responseResult = ResponseResult.success(data);
responseResult.put(PAGE_NUM, pageInfo.getPageNum());
responseResult.put(PAGE_SIZE, pageInfo.getPageSize());
responseResult.put(TOTAL, pageInfo.getTotal());
return responseResult;
}
/**
* 对象类型响应消息
* @param data
* @return
*/
public ResponseResult getResult(Object data) {
return ResponseResult.success(data);
}
}
package com.panda.web.controller.system;
import com.panda.common.response.ResponseResult;
import com.panda.system.domin.SysUser;
import com.panda.system.domin.vo.SysUserVo;
import com.panda.system.service.impl.SysUserServiceImpl;
import com.panda.web.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class SysUserController extends BaseController {
@Autowired
private SysUserServiceImpl sysUserService;
@GetMapping("/sysUser")
public ResponseResult findAllUsers(SysUser sysUser) {
startPage();
List<SysUser> data = sysUserService.findAllUsers(sysUser);
return getResult(data);
}
// 根据id查询
@GetMapping("/sysUser/{id}")
public ResponseResult findUserById(@PathVariable Long id) {
return getResult(sysUserService.findUserById(id));
}
/**
* 添加用户请求,注册也在这里
* @param sysUser
* @return
*/
@PostMapping("/sysUser")
public ResponseResult addUser(@Validated @RequestBody SysUser sysUser) {
return getResult(sysUserService.addUser(sysUser));
}
/**
*更新
* @param sysUser
* @return
*/
@PutMapping("/sysUser")
public ResponseResult updateUser(@Validated @RequestBody SysUser sysUser) {
return getResult(sysUserService.updateUser(sysUser));
}
/**
*删除
* @param ids
* @return
*/
@DeleteMapping("/sysUser/{ids}")
public ResponseResult deleteUser(@PathVariable Long[] ids) {
return getResult(sysUserService.deleteUser(ids));
}
/**
* 用户登录请求
*
* @param sysUserVo 封装用户登录输入的信息
* @return
*/
@RequestMapping("/sysUser/login")
public ResponseResult login(@RequestBody SysUserVo sysUserVo) {
return getResult(sysUserService.login(sysUserVo));
}
/**
* 用户注册请求
*
* @param sysUser
* @return
*/
@PostMapping("/sysUser/register")
public ResponseResult register(@Validated @RequestBody SysUser sysUser) {
// 注册只接收部分参数值,重新建立一个实例对象只接受注册接受的参数
SysUser registerUserInfo = new SysUser();
registerUserInfo.setUserName(sysUser.getUserName());
registerUserInfo.setPassword(sysUser.getPassword());
registerUserInfo.setSex(sysUser.getSex());
registerUserInfo.setPhoneNumber(sysUser.getPhoneNumber());
return getResult(sysUserService.addUser(registerUserInfo));
}
}
这个影院的基本信息
package com.panda.web.controller.system;
import com.panda.common.response.ResponseResult;
import com.panda.system.domin.SysCinema;
import com.panda.system.service.impl.SysCinemaServiceImpl;
import com.panda.web.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
@RestController
public class SysCinemaController extends BaseController {
@Autowired
private SysCinemaServiceImpl sysCinemaService;
@GetMapping("/sysCinema")
public ResponseResult findCinema() {
return getResult(sysCinemaService.findCinema());
}
@PutMapping("/sysCinema/update")
public ResponseResult updateCinema(@Validated @RequestBody SysCinema sysCinema) {
return getResult(sysCinemaService.updateCinema(sysCinema));
}
@GetMapping(value = {"/sysCinema/find/{cinemaId}/{movieId}", "/sysCinema/find/{cinemaId}"})
public ResponseResult findCinemaById(@PathVariable Long cinemaId, @PathVariable(required = false) Long movieId) {
SysCinema cinema = sysCinemaService.findCinemaById(cinemaId);
if (movieId == null || movieId == 0) {
movieId = cinema.getSysMovieList().size() > 0 ? cinema.getSysMovieList().get(0).getMovieId() : 0;
}
HashMap<String, Object> response = new HashMap<>();
// 将查到的信息放在响应体中
response.put("cinema", cinema);
return getResult(response);
}
}
package com.panda.web.controller.system;
import com.panda.common.response.ResponseResult;
import com.panda.system.domin.SysRole;
import com.panda.system.service.impl.SysRoleServiceImpl;
import com.panda.web.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class SysRoleController extends BaseController {
@Autowired
SysRoleServiceImpl sysRoleService;
@GetMapping("/sysRole")
public ResponseResult findAllRoles() {
startPage();
List<SysRole> data = sysRoleService.findAllRoles();
return getResult(data);
}
@GetMapping("/sysRole/{id}")
public ResponseResult findRoleById(@PathVariable Long id) {
return getResult(sysRoleService.findRoleById(id));
}
@PostMapping("/sysRole")
public ResponseResult addRole(@Validated @RequestBody SysRole sysRole) {
return getResult(sysRoleService.addRole(sysRole));
}
@PutMapping("/sysRole")
// @Validated 来检验数据,如果数据异常则抛出异常,统一处理。
public ResponseResult updateRole(@Validated @RequestBody SysRole sysRole) {
return getResult(sysRoleService.updateRole(sysRole));
}
@DeleteMapping("/sysRole/{ids}")
public ResponseResult deleteRole(@PathVariable Long[] ids) {
return getResult(sysRoleService.deleteRole(ids));
}
/**
* 给指定 id 的角色分配权限,包括增加或者删除权限
* @param roleId
* @param keys
* @return
*/
@PostMapping("/sysRole/{roleId}")
public ResponseResult allotRight(@PathVariable Long roleId, @RequestBody Long[] keys) {
return getResult(sysRoleService.allotRight(roleId, keys));
}
}
基本思路:
controller——>service——>dao——>xml
/**
* 用户注册请求
*
* @param sysUser
* @return
*/
@PostMapping("/sysUser/register")
public ResponseResult register(@Validated @RequestBody SysUser sysUser) {
// 注册只接收部分参数值,重新建立一个实例对象只接受注册接受的参数
SysUser registerUserInfo = new SysUser();
registerUserInfo.setUserName(sysUser.getUserName());
registerUserInfo.setPassword(sysUser.getPassword());
registerUserInfo.setSex(sysUser.getSex());
registerUserInfo.setPhoneNumber(sysUser.getPhoneNumber());
return getResult(sysUserService.addUser(registerUserInfo));
}
//处理密码:md5 + salt + hash散列
/**
* 处理注册逻辑
*
* @param sysUser
* @return
*/
@Override
public int addUser(SysUser sysUser) {
if (!isUserNameUnique(sysUser.getUserName(), -1L)) {
throw new AuthenticationException("用户名重复");
}
//处理密码:md5 + salt + hash散列
String salt = SaltUtils.getSalt(8);
Md5Hash md5Hash = new Md5Hash(sysUser.getPassword(), salt, 1024);
sysUser.setPassword(md5Hash.toHex());
sysUser.setSalt(salt);
return sysUserMapper.addUser(sysUser);
}
<update id="addUser" parameterType="SysUser">
<selectKey keyProperty="userId" keyColumn="user_id" resultType="long" order="AFTER">
select last_insert_id();
selectKey>
insert into sys_user(
user_name,
password,
salt,
<if test="email != null and email != ''">email,if>
<if test="phoneNumber != null and phoneNumber != ''">phone_number,if>
<if test="userPicture != null and userPicture != ''">user_picture,if>
<if test="roleId != null and roleId != 0"> role_id, if>
sex
)
values(
#{userName},
#{password},
#{salt},
<if test="email != null and email != ''">#{email},if>
<if test="phoneNumber != null and phoneNumber != ''">#{phoneNumber},if>
<if test="userPicture != null and userPicture != ''">#{userPicture},if>
<if test="roleId != null and roleId != 0">#{roleId}, if>
#{sex})
update>
/**
* 用户登录请求
*
* @param sysUserVo 封装用户登录输入的信息
* @return
*/
@RequestMapping("/sysUser/login")
public ResponseResult login(@RequestBody SysUserVo sysUserVo) {
return getResult(sysUserService.login(sysUserVo));
}
@Override
public LoginUser login(SysUserVo sysUserVo) {
//登录,先查询用户信息
SysUser user = sysUserMapper.findUserByName(sysUserVo.getUserName());
if (user == null) {
throw new AuthenticationException("用户名不存在");
}
//验证密码(拿到的password+salt)
Md5Hash md5Hash = new Md5Hash(sysUserVo.getPassword(), user.getSalt(), 1024);
if (!user.getPassword().equals(md5Hash.toHex())) {
throw new AuthenticationException("用户名或密码错误");
}
//设置登录用户对象
LoginUser loginUser = findLoginUser(sysUserVo);
//颁发token
String token = JwtUtil.sign(user.getUserName(), user.getPassword());
loginUser.setToken(token);
return loginUser;
}
@Override
public LoginUser findLoginUser(SysUserVo sysUserVo) {
return sysUserMapper.findLoginUser(sysUserVo);
}
<select id="findUserByName" resultMap="SysUserMap" parameterType="String">
select * from sys_user where user_name = #{userName} and del_state = 0
</select>
<!-- 查询登录用户信息 -->
<select id="findLoginUser" parameterType="SysUserVo" resultMap="loginUserMap">
select * from sys_user where user_name = #{userName} limit 0,1
</select>
package com.panda.system.domin;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class SysRole implements Serializable {
private static final Long serialVersionUID = 1L;
private Long roleId;
//角色名称
@NotBlank(message = "角色名称不能为空")
private String roleName;
//角色描述
@NotBlank(message = "角色描述不能为空")
private String roleDesc;
//角色拥有的权限,分多级权限存储,取名为children方便读取所有权限
private List<SysResource> children;
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.panda.system.mapper.SysRoleMapper">
<resultMap id="SysRoleMap" type="SysRole">
<id property="roleId" column="role_id">id>
<result property="roleName" column="role_name">result>
<result property="roleDesc" column="role_desc">result>
<collection property="children" column="role_id" ofType="SysResource" select="com.panda.system.mapper.SysRoleMapper.findByRoleId">
collection>
resultMap>
<resultMap id="OneRoleMap" type="SysResource">
<id property="id" column="resource_id">id>
<result property="name" column="resource_name">result>
<result property="path" column="path">result>
<result property="level" column="level">result>
<result property="parentId" column="parent_id">result>
<collection property="children" column="resource_id" ofType="SysResource">
<id property="id" column="sid">id>
<result property="name" column="sname">result>
<result property="path" column="spath">result>
<result property="level" column="slevel">result>
<result property="parentId" column="sparent_id">result>
<collection property="children" column="resource_id" ofType="SysResource">
<id property="id" column="gsid">id>
<result property="name" column="gsname">result>
<result property="path" column="gspath">result>
<result property="level" column="gslevel">result>
<result property="parentId" column="gsparent_id">result>
collection>
collection>
resultMap>
<select id="findAllRoles" resultMap="SysRoleMap">
select * from sys_role sysr
select>
<select id="findByRoleId" resultMap="OneRoleMap" parameterType="Long">
select resources.* from sys_role sysr
left join sys_role_resource srr on sysr.role_id = srr.role_id
left join
(select sr.*, sr1.resource_id sid, sr1.resource_name sname, sr1.path spath, sr1.level slevel, sr1.parent_id sparent_id,
sr2.resource_id gsid, sr2.resource_name gsname, sr2.path gspath, sr2.level gslevel, sr2.parent_id gsparent_id from sys_resource sr
left join sys_resource sr1 on sr1.parent_id = sr.resource_id
left join sys_resource sr2 on sr2.parent_id = sr1.resource_id
where sr.level = 1) resources on srr.resource_id = resources.gsid where sysr.role_id = #{id}
select>
<select id="findRoleById" resultType="SysRole" parameterType="long">
select * from sys_role where role_id = #{id}
select>
<update id="addRole" parameterType="SysRole">
insert into sys_role(role_name, role_desc) values(#{roleName}, #{roleDesc})
update>
<update id="updateRole" parameterType="SysRole">
update sys_role set role_name = #{roleName}, role_desc = #{roleDesc} where role_id = #{roleId}
update>
<update id="deleteRole" parameterType="Long">
delete from sys_role where role_id = #{id}
update>
<update id="addRight">
insert into sys_role_resource values(#{roleId}, #{resourceId})
update>
<update id="deleteRight">
delete from sys_role_resource where role_id = #{roleId} and resource_id = #{resourceId}
update>
<select id="findAllRights" resultType="Long" parameterType="Long">
select resource_id id from sys_role_resource where role_id = #{roleId}
select>
mapper>