Commit ea4c9e498164f992582634471ddb47a056aad0c1

Authored by YunaiV
1 parent dba723b8

1. 修改 MenuDO 的字段,menuId 改成 id,menuName 改成 name

2. 添加 Menu 的创建、修改、删除接口
Showing 29 changed files with 503 additions and 237 deletions
ruoyi-ui/src/api/system/menu.js
@@ -10,9 +10,9 @@ export function listMenu(query) { @@ -10,9 +10,9 @@ export function listMenu(query) {
10 } 10 }
11 11
12 // 查询菜单详细 12 // 查询菜单详细
13 -export function getMenu(menuId) { 13 +export function getMenu(id) {
14 return request({ 14 return request({
15 - url: '/system/menu/' + menuId, 15 + url: '/system/menu/get?id=' + id,
16 method: 'get' 16 method: 'get'
17 }) 17 })
18 } 18 }
@@ -36,7 +36,7 @@ export function roleMenuTreeselect(roleId) { @@ -36,7 +36,7 @@ export function roleMenuTreeselect(roleId) {
36 // 新增菜单 36 // 新增菜单
37 export function addMenu(data) { 37 export function addMenu(data) {
38 return request({ 38 return request({
39 - url: '/system/menu', 39 + url: '/system/menu/create',
40 method: 'post', 40 method: 'post',
41 data: data 41 data: data
42 }) 42 })
@@ -45,16 +45,16 @@ export function addMenu(data) { @@ -45,16 +45,16 @@ export function addMenu(data) {
45 // 修改菜单 45 // 修改菜单
46 export function updateMenu(data) { 46 export function updateMenu(data) {
47 return request({ 47 return request({
48 - url: '/system/menu',  
49 - method: 'put', 48 + url: '/system/menu/update',
  49 + method: 'post',
50 data: data 50 data: data
51 }) 51 })
52 } 52 }
53 53
54 // 删除菜单 54 // 删除菜单
55 -export function delMenu(menuId) { 55 +export function delMenu(id) {
56 return request({ 56 return request({
57 - url: '/system/menu/' + menuId,  
58 - method: 'delete' 57 + url: '/system/menu/delete?id=' + id,
  58 + method: 'post'
59 }) 59 })
60 -}  
61 \ No newline at end of file 60 \ No newline at end of file
  61 +}
ruoyi-ui/src/store/modules/dict.js
@@ -28,8 +28,8 @@ const actions = { @@ -28,8 +28,8 @@ const actions = {
28 } 28 }
29 // 处理 dictValue 层级 29 // 处理 dictValue 层级
30 dictDataMap[dictData.dictType].push({ 30 dictDataMap[dictData.dictType].push({
31 - dictValue: dictData.dictValue,  
32 - dictLabel: dictData.dictLabel 31 + value: dictData.value,
  32 + label: dictData.label
33 }) 33 })
34 }) 34 })
35 // 存储到 Store 中 35 // 存储到 Store 中
ruoyi-ui/src/store/modules/permission.js
@@ -44,7 +44,7 @@ function filterAsyncRouter(asyncRouterMap, isRewrite = false) { @@ -44,7 +44,7 @@ function filterAsyncRouter(asyncRouterMap, isRewrite = false) {
44 // 将 ruoyi 后端原有耦合前端的逻辑,迁移到此处 44 // 将 ruoyi 后端原有耦合前端的逻辑,迁移到此处
45 // 处理 meta 属性 45 // 处理 meta 属性
46 route.meta = { 46 route.meta = {
47 - title: route.menuName, 47 + title: route.name,
48 icon: route.icon 48 icon: route.icon
49 } 49 }
50 // 处理 component 属性 50 // 处理 component 属性
ruoyi-ui/src/utils/constants.js 0 → 100644
  1 +/**
  2 + * Created by 芋道源码
  3 + *
  4 + * 枚举类
  5 + */
  6 +
  7 +/**
  8 + * 全局通用状态枚举
  9 + */
  10 +export const SysCommonStatusEnum = {
  11 + ENABLE: 0, // 开启
  12 + DISABLE: 1 // 禁用
  13 +}
  14 +
  15 +/**
  16 + * 菜单的类型枚举
  17 + */
  18 +export const SysMenuTypeEnum = {
  19 + DIR : 1, // 目录
  20 + MENU: 2, // 菜单
  21 + BUTTON: 3 // 按钮
  22 +}
  23 +
  24 +/**
  25 + * 角色的类型枚举
  26 + */
  27 +export const RoleTypeEnum = {
  28 + SYSTEM: 1, // 内置角色
  29 + CUSTOM: 2 // 自定义角色
  30 +}
ruoyi-ui/src/utils/dict.js
@@ -6,7 +6,8 @@ @@ -6,7 +6,8 @@
6 import store from '@/store' 6 import store from '@/store'
7 7
8 export const DICT_TYPE = { 8 export const DICT_TYPE = {
9 - SYS_COMMON_STATUS: 'sys_common_status' 9 + SYS_COMMON_STATUS: 'sys_common_status',
  10 + SYS_MENU_TYPE: 'menu_type'
10 } 11 }
11 12
12 /** 13 /**
@@ -28,8 +29,8 @@ export function getDictDataLabel(dictType, value) { @@ -28,8 +29,8 @@ export function getDictDataLabel(dictType, value) {
28 // 获取 value 对应的展示名 29 // 获取 value 对应的展示名
29 value = value + '' // 强制转换成字符串,因为 DictData 小类数值,是字符串 30 value = value + '' // 强制转换成字符串,因为 DictData 小类数值,是字符串
30 for (const dictData of dictDatas) { 31 for (const dictData of dictDatas) {
31 - if (dictData.dictValue === value) {  
32 - return dictData.dictLabel 32 + if (dictData.value === value) {
  33 + return dictData.label
33 } 34 }
34 } 35 }
35 return '' 36 return ''
ruoyi-ui/src/views/system/menu/index.vue
1 <template> 1 <template>
2 <div class="app-container"> 2 <div class="app-container">
3 <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch"> 3 <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
4 - <el-form-item label="菜单名称" prop="menuName"> 4 + <el-form-item label="菜单名称" prop="name">
5 <el-input 5 <el-input
6 - v-model="queryParams.menuName" 6 + v-model="queryParams.name"
7 placeholder="请输入菜单名称" 7 placeholder="请输入菜单名称"
8 clearable 8 clearable
9 size="small" 9 size="small"
@@ -42,10 +42,10 @@ @@ -42,10 +42,10 @@
42 <el-table 42 <el-table
43 v-loading="loading" 43 v-loading="loading"
44 :data="menuList" 44 :data="menuList"
45 - row-key="menuId" 45 + row-key="id"
46 :tree-props="{children: 'children', hasChildren: 'hasChildren'}" 46 :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
47 > 47 >
48 - <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column> 48 + <el-table-column prop="name" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
49 <el-table-column prop="icon" label="图标" align="center" width="100"> 49 <el-table-column prop="icon" label="图标" align="center" width="100">
50 <template slot-scope="scope"> 50 <template slot-scope="scope">
51 <svg-icon :icon-class="scope.row.icon" /> 51 <svg-icon :icon-class="scope.row.icon" />
@@ -102,16 +102,18 @@ @@ -102,16 +102,18 @@
102 </el-form-item> 102 </el-form-item>
103 </el-col> 103 </el-col>
104 <el-col :span="24"> 104 <el-col :span="24">
105 - <el-form-item label="菜单类型" prop="menuType">  
106 - <el-radio-group v-model="form.menuType">  
107 - <el-radio label="1">目录</el-radio>  
108 - <el-radio label="2">菜单</el-radio>  
109 - <el-radio label="3">按钮</el-radio> 105 + <el-form-item label="菜单类型" prop="type">
  106 + <el-radio-group v-model="form.type">
  107 + <el-radio
  108 + v-for="dict in menuTypeDictDatas"
  109 + :key="dict.value"
  110 + :label="dict.value"
  111 + >{{dict.label}}</el-radio>
110 </el-radio-group> 112 </el-radio-group>
111 </el-form-item> 113 </el-form-item>
112 </el-col> 114 </el-col>
113 <el-col :span="24"> 115 <el-col :span="24">
114 - <el-form-item v-if="form.menuType != '3'" label="菜单图标"> 116 + <el-form-item v-if="form.type != '3'" label="菜单图标">
115 <el-popover 117 <el-popover
116 placement="bottom-start" 118 placement="bottom-start"
117 width="460" 119 width="460"
@@ -133,65 +135,38 @@ @@ -133,65 +135,38 @@
133 </el-form-item> 135 </el-form-item>
134 </el-col> 136 </el-col>
135 <el-col :span="12"> 137 <el-col :span="12">
136 - <el-form-item label="菜单名称" prop="menuName">  
137 - <el-input v-model="form.menuName" placeholder="请输入菜单名称" /> 138 + <el-form-item label="菜单名称" prop="name">
  139 + <el-input v-model="form.name" placeholder="请输入菜单名称" />
138 </el-form-item> 140 </el-form-item>
139 </el-col> 141 </el-col>
140 <el-col :span="12"> 142 <el-col :span="12">
141 - <el-form-item label="显示排序" prop="orderNum">  
142 - <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />  
143 - </el-form-item>  
144 - </el-col>  
145 - <el-col :span="12">  
146 - <el-form-item v-if="form.menuType != '3'" label="是否外链">  
147 - <el-radio-group v-model="form.isFrame">  
148 - <el-radio label="0">是</el-radio>  
149 - <el-radio label="1">否</el-radio>  
150 - </el-radio-group> 143 + <el-form-item label="显示排序" prop="sort">
  144 + <el-input-number v-model="form.sort" controls-position="right" :min="0" />
151 </el-form-item> 145 </el-form-item>
152 </el-col> 146 </el-col>
153 <el-col :span="12"> 147 <el-col :span="12">
154 - <el-form-item v-if="form.menuType != '3'" label="路由地址" prop="path"> 148 + <el-form-item v-if="form.type != '3'" label="路由地址" prop="path">
155 <el-input v-model="form.path" placeholder="请输入路由地址" /> 149 <el-input v-model="form.path" placeholder="请输入路由地址" />
156 </el-form-item> 150 </el-form-item>
157 </el-col> 151 </el-col>
158 - <el-col :span="12" v-if="form.menuType == '2'"> 152 + <el-col :span="12" v-if="form.type == '2'">
159 <el-form-item label="组件路径" prop="component"> 153 <el-form-item label="组件路径" prop="component">
160 <el-input v-model="form.component" placeholder="请输入组件路径" /> 154 <el-input v-model="form.component" placeholder="请输入组件路径" />
161 </el-form-item> 155 </el-form-item>
162 </el-col> 156 </el-col>
163 <el-col :span="12"> 157 <el-col :span="12">
164 - <el-form-item v-if="form.menuType != '1'" label="权限标识"> 158 + <el-form-item v-if="form.type != '1'" label="权限标识">
165 <el-input v-model="form.perms" placeholder="请权限标识" maxlength="50" /> 159 <el-input v-model="form.perms" placeholder="请权限标识" maxlength="50" />
166 </el-form-item> 160 </el-form-item>
167 </el-col> 161 </el-col>
168 <el-col :span="12"> 162 <el-col :span="12">
169 - <el-form-item v-if="form.menuType != '3'" label="显示状态">  
170 - <el-radio-group v-model="form.visible">  
171 - <el-radio  
172 - v-for="dict in visibleOptions"  
173 - :key="dict.dictValue"  
174 - :label="dict.dictValue"  
175 - >{{dict.dictLabel}}</el-radio>  
176 - </el-radio-group>  
177 - </el-form-item>  
178 - </el-col>  
179 - <el-col :span="12">  
180 - <el-form-item v-if="form.menuType != '3'" label="菜单状态"> 163 + <el-form-item v-if="form.type != '3'" label="菜单状态">
181 <el-radio-group v-model="form.status"> 164 <el-radio-group v-model="form.status">
182 <el-radio 165 <el-radio
183 - v-for="dict in statusOptions"  
184 - :key="dict.dictValue"  
185 - :label="dict.dictValue"  
186 - >{{dict.dictLabel}}</el-radio>  
187 - </el-radio-group>  
188 - </el-form-item>  
189 - </el-col>  
190 - <el-col :span="12">  
191 - <el-form-item v-if="form.menuType == '2'" label="是否缓存">  
192 - <el-radio-group v-model="form.isCache">  
193 - <el-radio label="0">缓存</el-radio>  
194 - <el-radio label="1">不缓存</el-radio> 166 + v-for="dict in statusDictDatas"
  167 + :key="dict.value"
  168 + :label="dict.value"
  169 + >{{dict.label}}</el-radio>
195 </el-radio-group> 170 </el-radio-group>
196 </el-form-item> 171 </el-form-item>
197 </el-col> 172 </el-col>
@@ -211,6 +186,7 @@ import Treeselect from &quot;@riophae/vue-treeselect&quot;; @@ -211,6 +186,7 @@ import Treeselect from &quot;@riophae/vue-treeselect&quot;;
211 import "@riophae/vue-treeselect/dist/vue-treeselect.css"; 186 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
212 import IconSelect from "@/components/IconSelect"; 187 import IconSelect from "@/components/IconSelect";
213 188
  189 +import { SysMenuTypeEnum, SysCommonStatusEnum } from '@/utils/constants'
214 import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict' 190 import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
215 191
216 export default { 192 export default {
@@ -230,29 +206,34 @@ export default { @@ -230,29 +206,34 @@ export default {
230 title: "", 206 title: "",
231 // 是否显示弹出层 207 // 是否显示弹出层
232 open: false, 208 open: false,
233 - // 显示状态数据字典  
234 - visibleOptions: [],  
235 // 菜单状态数据字典 209 // 菜单状态数据字典
236 statusOptions: [], 210 statusOptions: [],
237 // 查询参数 211 // 查询参数
238 queryParams: { 212 queryParams: {
239 - menuName: undefined, 213 + name: undefined,
240 visible: undefined 214 visible: undefined
241 }, 215 },
242 // 表单参数 216 // 表单参数
243 form: {}, 217 form: {},
244 // 表单校验 218 // 表单校验
245 rules: { 219 rules: {
246 - menuName: [ 220 + name: [
247 { required: true, message: "菜单名称不能为空", trigger: "blur" } 221 { required: true, message: "菜单名称不能为空", trigger: "blur" }
248 ], 222 ],
249 - orderNum: [ 223 + sort: [
250 { required: true, message: "菜单顺序不能为空", trigger: "blur" } 224 { required: true, message: "菜单顺序不能为空", trigger: "blur" }
251 ], 225 ],
252 path: [ 226 path: [
253 { required: true, message: "路由地址不能为空", trigger: "blur" } 227 { required: true, message: "路由地址不能为空", trigger: "blur" }
254 ] 228 ]
255 - } 229 + },
  230 +
  231 + // 枚举
  232 + MenuTypeEnum: SysMenuTypeEnum,
  233 + CommonStatusEnum: SysCommonStatusEnum,
  234 + // 数据字典
  235 + menuTypeDictDatas: getDictDatas(DICT_TYPE.SYS_MENU_TYPE),
  236 + statusDictDatas: getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)
256 }; 237 };
257 }, 238 },
258 created() { 239 created() {
@@ -267,7 +248,7 @@ export default { @@ -267,7 +248,7 @@ export default {
267 getList() { 248 getList() {
268 this.loading = true; 249 this.loading = true;
269 listMenu(this.queryParams).then(response => { 250 listMenu(this.queryParams).then(response => {
270 - this.menuList = this.handleTree(response.data, "menuId"); 251 + this.menuList = this.handleTree(response.data, "id");
271 this.loading = false; 252 this.loading = false;
272 }); 253 });
273 }, 254 },
@@ -277,8 +258,8 @@ export default { @@ -277,8 +258,8 @@ export default {
277 delete node.children; 258 delete node.children;
278 } 259 }
279 return { 260 return {
280 - id: node.menuId,  
281 - label: node.menuName, 261 + id: node.id,
  262 + label: node.name,
282 children: node.children 263 children: node.children
283 }; 264 };
284 }, 265 },
@@ -286,8 +267,8 @@ export default { @@ -286,8 +267,8 @@ export default {
286 getTreeselect() { 267 getTreeselect() {
287 listMenu().then(response => { 268 listMenu().then(response => {
288 this.menuOptions = []; 269 this.menuOptions = [];
289 - const menu = { menuId: 0, menuName: '主类目', children: [] };  
290 - menu.children = this.handleTree(response.data, "menuId"); 270 + const menu = { id: 0, name: '主类目', children: [] };
  271 + menu.children = this.handleTree(response.data, "id");
291 this.menuOptions.push(menu); 272 this.menuOptions.push(menu);
292 }); 273 });
293 }, 274 },
@@ -303,15 +284,12 @@ export default { @@ -303,15 +284,12 @@ export default {
303 // 表单重置 284 // 表单重置
304 reset() { 285 reset() {
305 this.form = { 286 this.form = {
306 - menuId: undefined, 287 + id: undefined,
307 parentId: 0, 288 parentId: 0,
308 - menuName: undefined, 289 + name: undefined,
309 icon: undefined, 290 icon: undefined,
310 - menuType: "1",  
311 - orderNum: undefined,  
312 - isFrame: "1",  
313 - isCache: "0",  
314 - visible: "0", 291 + type: "1",
  292 + sort: undefined,
315 status: "0" 293 status: "0"
316 }; 294 };
317 this.resetForm("form"); 295 this.resetForm("form");
@@ -329,8 +307,8 @@ export default { @@ -329,8 +307,8 @@ export default {
329 handleAdd(row) { 307 handleAdd(row) {
330 this.reset(); 308 this.reset();
331 this.getTreeselect(); 309 this.getTreeselect();
332 - if (row != null && row.menuId) {  
333 - this.form.parentId = row.menuId; 310 + if (row != null && row.id) {
  311 + this.form.parentId = row.id;
334 } else { 312 } else {
335 this.form.parentId = 0; 313 this.form.parentId = 0;
336 } 314 }
@@ -341,7 +319,7 @@ export default { @@ -341,7 +319,7 @@ export default {
341 handleUpdate(row) { 319 handleUpdate(row) {
342 this.reset(); 320 this.reset();
343 this.getTreeselect(); 321 this.getTreeselect();
344 - getMenu(row.menuId).then(response => { 322 + getMenu(row.id).then(response => {
345 this.form = response.data; 323 this.form = response.data;
346 this.open = true; 324 this.open = true;
347 this.title = "修改菜单"; 325 this.title = "修改菜单";
@@ -351,7 +329,24 @@ export default { @@ -351,7 +329,24 @@ export default {
351 submitForm: function() { 329 submitForm: function() {
352 this.$refs["form"].validate(valid => { 330 this.$refs["form"].validate(valid => {
353 if (valid) { 331 if (valid) {
354 - if (this.form.menuId != undefined) { 332 + // 若权限类型为菜单时,进行 route 的校验,避免后续拼接出来的路由无法跳转
  333 + if (this.form.type === ResourceTypeEnum.MENU) {
  334 + // 如果是外链,则不进行校验
  335 + const route = this.resourceForm.route
  336 + if (route.indexOf('http://') === -1 || route.indexOf('https://') === -1) {
  337 + // 父权限为根节点,route 必须以 / 开头
  338 + if (this.resourceForm.pid === 0 && route.charAt(0) !== '/') {
  339 + this.messageSuccess('前端必须以 / 开头')
  340 + return
  341 + } else if (this.resourceForm.pid !== 0 && route.charAt(0) === '/') {
  342 + this.messageSuccess('前端不能以 / 开头')
  343 + return
  344 + }
  345 + }
  346 + }
  347 +
  348 + // 提交
  349 + if (this.form.id !== undefined) {
355 updateMenu(this.form).then(response => { 350 updateMenu(this.form).then(response => {
356 this.msgSuccess("修改成功"); 351 this.msgSuccess("修改成功");
357 this.open = false; 352 this.open = false;
@@ -369,12 +364,12 @@ export default { @@ -369,12 +364,12 @@ export default {
369 }, 364 },
370 /** 删除按钮操作 */ 365 /** 删除按钮操作 */
371 handleDelete(row) { 366 handleDelete(row) {
372 - this.$confirm('是否确认删除名称为"' + row.menuName + '"的数据项?', "警告", { 367 + this.$confirm('是否确认删除名称为"' + row.name + '"的数据项?', "警告", {
373 confirmButtonText: "确定", 368 confirmButtonText: "确定",
374 cancelButtonText: "取消", 369 cancelButtonText: "取消",
375 type: "warning" 370 type: "warning"
376 }).then(function() { 371 }).then(function() {
377 - return delMenu(row.menuId); 372 + return delMenu(row.id);
378 }).then(() => { 373 }).then(() => {
379 this.getList(); 374 this.getList();
380 this.msgSuccess("删除成功"); 375 this.msgSuccess("删除成功");
ruoyi-ui/src/views/system/role/index.vue
@@ -437,7 +437,7 @@ export default { @@ -437,7 +437,7 @@ export default {
437 roleKey: undefined, 437 roleKey: undefined,
438 roleSort: 0, 438 roleSort: 0,
439 status: "0", 439 status: "0",
440 - menuIds: [], 440 + ids: [],
441 deptIds: [], 441 deptIds: [],
442 menuCheckStrictly: true, 442 menuCheckStrictly: true,
443 deptCheckStrictly: true, 443 deptCheckStrictly: true,
@@ -535,14 +535,14 @@ export default { @@ -535,14 +535,14 @@ export default {
535 this.$refs["form"].validate(valid => { 535 this.$refs["form"].validate(valid => {
536 if (valid) { 536 if (valid) {
537 if (this.form.roleId != undefined) { 537 if (this.form.roleId != undefined) {
538 - this.form.menuIds = this.getMenuAllCheckedKeys(); 538 + this.form.ids = this.getMenuAllCheckedKeys();
539 updateRole(this.form).then(response => { 539 updateRole(this.form).then(response => {
540 this.msgSuccess("修改成功"); 540 this.msgSuccess("修改成功");
541 this.open = false; 541 this.open = false;
542 this.getList(); 542 this.getList();
543 }); 543 });
544 } else { 544 } else {
545 - this.form.menuIds = this.getMenuAllCheckedKeys(); 545 + this.form.ids = this.getMenuAllCheckedKeys();
546 addRole(this.form).then(response => { 546 addRole(this.form).then(response => {
547 this.msgSuccess("新增成功"); 547 this.msgSuccess("新增成功");
548 this.open = false; 548 this.open = false;
@@ -592,4 +592,4 @@ export default { @@ -592,4 +592,4 @@ export default {
592 } 592 }
593 } 593 }
594 }; 594 };
595 -</script>  
596 \ No newline at end of file 595 \ No newline at end of file
  596 +</script>
ruoyi-ui/src/views/tool/gen/editTable.vue
@@ -167,7 +167,7 @@ export default { @@ -167,7 +167,7 @@ export default {
167 }); 167 });
168 /** 查询菜单下拉列表 */ 168 /** 查询菜单下拉列表 */
169 getMenuTreeselect().then(response => { 169 getMenuTreeselect().then(response => {
170 - this.menus = this.handleTree(response.data, "menuId"); 170 + this.menus = this.handleTree(response.data, "id");
171 }); 171 });
172 } 172 }
173 }, 173 },
ruoyi-ui/src/views/tool/gen/genInfoForm.vue
@@ -219,8 +219,8 @@ export default { @@ -219,8 +219,8 @@ export default {
219 delete node.children; 219 delete node.children;
220 } 220 }
221 return { 221 return {
222 - id: node.menuId,  
223 - label: node.menuName, 222 + id: node.id,
  223 + label: node.name,
224 children: node.children 224 children: node.children
225 }; 225 };
226 } 226 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthMenuRespVO.java
1 package cn.iocoder.dashboard.modules.system.controller.auth.vo; 1 package cn.iocoder.dashboard.modules.system.controller.auth.vo;
2 2
3 import io.swagger.annotations.ApiModel; 3 import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
4 import lombok.AllArgsConstructor; 5 import lombok.AllArgsConstructor;
5 import lombok.Builder; 6 import lombok.Builder;
6 import lombok.Data; 7 import lombok.Data;
@@ -15,32 +16,22 @@ import java.util.List; @@ -15,32 +16,22 @@ import java.util.List;
15 @Builder 16 @Builder
16 public class SysAuthMenuRespVO { 17 public class SysAuthMenuRespVO {
17 18
18 - /**  
19 - * 菜单编号  
20 - */  
21 - private Long menuId;  
22 - /**  
23 - * 父菜单编号  
24 - */ 19 + @ApiModelProperty(value = "菜单名称", required = true, example = "芋道")
  20 + private Long id;
  21 +
  22 + @ApiModelProperty(value = "父菜单 ID", required = true, example = "1024")
25 private Long parentId; 23 private Long parentId;
26 24
27 - /**  
28 - * 菜单名称  
29 - */  
30 - private String menuName; 25 + @ApiModelProperty(value = "菜单名称", required = true, example = "芋道")
  26 + private String name;
31 27
32 - /**  
33 - * 路由地址  
34 - */ 28 + @ApiModelProperty(value = "路由地址", example = "post", notes = "仅菜单类型为菜单或者目录时,才需要传")
35 private String path; 29 private String path;
36 - /**  
37 - * 组件地址  
38 - */ 30 +
  31 + @ApiModelProperty(value = "组件路径", example = "system/post/index", notes = "仅菜单类型为菜单时,才需要传")
39 private String component; 32 private String component;
40 33
41 - /**  
42 - * 菜单图标  
43 - */ 34 + @ApiModelProperty(value = "菜单图标", example = "/menu/list", notes = "仅菜单类型为菜单或者目录时,才需要传")
44 private String icon; 35 private String icon;
45 36
46 /** 37 /**
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/vo/SysDataDictSimpleVO.java
@@ -12,9 +12,9 @@ public class SysDataDictSimpleVO { @@ -12,9 +12,9 @@ public class SysDataDictSimpleVO {
12 private String dictType; 12 private String dictType;
13 13
14 @ApiModelProperty(value = "字典键值", required = true, example = "1") 14 @ApiModelProperty(value = "字典键值", required = true, example = "1")
15 - private String dictValue; 15 + private String value;
16 16
17 @ApiModelProperty(value = "字典标签", required = true, example = "男") 17 @ApiModelProperty(value = "字典标签", required = true, example = "男")
18 - private String dictLabel; 18 + private String label;
19 19
20 } 20 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/SysMenuController.java
1 package cn.iocoder.dashboard.modules.system.controller.permission; 1 package cn.iocoder.dashboard.modules.system.controller.permission;
2 2
3 import cn.iocoder.dashboard.common.pojo.CommonResult; 3 import cn.iocoder.dashboard.common.pojo.CommonResult;
  4 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO; 5 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO;
5 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO; 6 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
  8 +import cn.iocoder.dashboard.modules.system.convert.permission.SysMenuConvert;
  9 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
6 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; 10 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
7 import io.swagger.annotations.Api; 11 import io.swagger.annotations.Api;
8 -import org.springframework.web.bind.annotation.GetMapping;  
9 -import org.springframework.web.bind.annotation.RequestMapping;  
10 -import org.springframework.web.bind.annotation.RestController; 12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.validation.annotation.Validated;
  14 +import org.springframework.web.bind.annotation.*;
11 15
12 import javax.annotation.Resource; 16 import javax.annotation.Resource;
13 import java.util.List; 17 import java.util.List;
@@ -22,24 +26,21 @@ public class SysMenuController { @@ -22,24 +26,21 @@ public class SysMenuController {
22 @Resource 26 @Resource
23 private SysMenuService menuService; 27 private SysMenuService menuService;
24 28
25 - /**  
26 - * 获取菜单列表  
27 - */ 29 + @ApiOperation("获取菜单列表")
28 // @PreAuthorize("@ss.hasPermi('system:menu:list')") 30 // @PreAuthorize("@ss.hasPermi('system:menu:list')")
29 @GetMapping("/list") 31 @GetMapping("/list")
30 public CommonResult<List<SysMenuRespVO>> list(SysMenuListReqVO reqVO) { 32 public CommonResult<List<SysMenuRespVO>> list(SysMenuListReqVO reqVO) {
31 return success(menuService.listMenus(reqVO)); 33 return success(menuService.listMenus(reqVO));
32 } 34 }
33 -//  
34 -// /**  
35 -// * 根据菜单编号获取详细信息  
36 -// */ 35 +
  36 + @ApiOperation("获取菜单信息")
  37 + @GetMapping("/get")
37 // @PreAuthorize("@ss.hasPermi('system:menu:query')") 38 // @PreAuthorize("@ss.hasPermi('system:menu:query')")
38 -// @GetMapping(value = "/{menuId}")  
39 -// public AjaxResult getInfo(@PathVariable Long menuId) {  
40 -// return AjaxResult.success(menuService.selectMenuById(menuId));  
41 -// }  
42 -// 39 + public CommonResult<SysMenuRespVO> getMenu(Long id) {
  40 + SysMenuDO menu = menuService.getMenu(id);
  41 + return success(SysMenuConvert.INSTANCE.convert(menu));
  42 + }
  43 +
43 // /** 44 // /**
44 // * 获取菜单下拉树列表 45 // * 获取菜单下拉树列表
45 // */ 46 // */
@@ -63,57 +64,31 @@ public class SysMenuController { @@ -63,57 +64,31 @@ public class SysMenuController {
63 // ajax.put("menus", menuService.buildMenuTreeSelect(menus)); 64 // ajax.put("menus", menuService.buildMenuTreeSelect(menus));
64 // return ajax; 65 // return ajax;
65 // } 66 // }
66 -//  
67 -// /**  
68 -// * 新增菜单  
69 -// */ 67 +
  68 + @ApiOperation("新增菜单")
70 // @PreAuthorize("@ss.hasPermi('system:menu:add')") 69 // @PreAuthorize("@ss.hasPermi('system:menu:add')")
71 // @Log(title = "菜单管理", businessType = BusinessType.INSERT) 70 // @Log(title = "菜单管理", businessType = BusinessType.INSERT)
72 -// @PostMapping  
73 -// public AjaxResult add(@Validated @RequestBody SysMenu menu) {  
74 -// if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {  
75 -// return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");  
76 -// } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())  
77 -// && !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) {  
78 -// return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");  
79 -// }  
80 -// menu.setCreateBy(SecurityUtils.getUsername());  
81 -// return toAjax(menuService.insertMenu(menu));  
82 -// }  
83 -//  
84 -// /**  
85 -// * 修改菜单  
86 -// */ 71 + @PostMapping("/create")
  72 + public CommonResult<Long> createMenu(@Validated @RequestBody SysMenuCreateReqVO reqVO) {
  73 + Long menuId = menuService.createMenu(reqVO);
  74 + return success(menuId);
  75 + }
  76 +
  77 + @ApiOperation("修改菜单")
87 // @PreAuthorize("@ss.hasPermi('system:menu:edit')") 78 // @PreAuthorize("@ss.hasPermi('system:menu:edit')")
88 // @Log(title = "菜单管理", businessType = BusinessType.UPDATE) 79 // @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
89 -// @PutMapping  
90 -// public AjaxResult edit(@Validated @RequestBody SysMenu menu) {  
91 -// if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {  
92 -// return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");  
93 -// } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())  
94 -// && !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) {  
95 -// return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");  
96 -// } else if (menu.getMenuId().equals(menu.getParentId())) {  
97 -// return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");  
98 -// }  
99 -// menu.setUpdateBy(SecurityUtils.getUsername());  
100 -// return toAjax(menuService.updateMenu(menu));  
101 -// }  
102 -//  
103 -// /**  
104 -// * 删除菜单  
105 -// */  
106 -// @PreAuthorize("@ss.hasPermi('system:menu:remove')") 80 + @PostMapping("/update")
  81 + public CommonResult<Boolean> updateMenu(@Validated @RequestBody SysMenuUpdateReqVO reqVO) {
  82 + menuService.updateMenu(reqVO);
  83 + return success(true);
  84 + }
  85 +
  86 + @ApiOperation("删除菜单")
107 // @Log(title = "菜单管理", businessType = BusinessType.DELETE) 87 // @Log(title = "菜单管理", businessType = BusinessType.DELETE)
108 // @DeleteMapping("/{menuId}") 88 // @DeleteMapping("/{menuId}")
109 -// public AjaxResult remove(@PathVariable("menuId") Long menuId) {  
110 -// if (menuService.hasChildByMenuId(menuId)) {  
111 -// return AjaxResult.error("存在子菜单,不允许删除");  
112 -// }  
113 -// if (menuService.checkMenuExistRole(menuId)) {  
114 -// return AjaxResult.error("菜单已分配,不允许删除");  
115 -// }  
116 -// return toAjax(menuService.deleteMenuById(menuId));  
117 -// } 89 + public CommonResult<Boolean> deleteMenu(@RequestParam("id") Long id) {
  90 + menuService.deleteMenu(id);
  91 + return success(true);
  92 + }
118 93
119 } 94 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/SysMenuBaseVO.java
@@ -17,15 +17,15 @@ public class SysMenuBaseVO { @@ -17,15 +17,15 @@ public class SysMenuBaseVO {
17 @ApiModelProperty(value = "菜单名称", required = true, example = "芋道") 17 @ApiModelProperty(value = "菜单名称", required = true, example = "芋道")
18 @NotBlank(message = "菜单名称不能为空") 18 @NotBlank(message = "菜单名称不能为空")
19 @Size(max = 50, message = "菜单名称长度不能超过50个字符") 19 @Size(max = 50, message = "菜单名称长度不能超过50个字符")
20 - private String menuName; 20 + private String name;
21 21
22 @ApiModelProperty(value = "权限标识", example = "sys:menu:add", notes = "仅菜单类型为按钮时,才需要传递") 22 @ApiModelProperty(value = "权限标识", example = "sys:menu:add", notes = "仅菜单类型为按钮时,才需要传递")
23 @Size(max = 100) 23 @Size(max = 100)
24 private String permission; 24 private String permission;
25 25
26 - @ApiModelProperty(value = "类型", required = true, example = "1", notes = "参见 MenuTypeEnum 枚举类") 26 + @ApiModelProperty(value = "类型", required = true, example = "1", notes = "参见 SysMenuTypeEnum 枚举类")
27 @NotBlank(message = "菜单类型不能为空") 27 @NotBlank(message = "菜单类型不能为空")
28 - private Integer menuType; 28 + private Integer type;
29 29
30 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") 30 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024")
31 @NotBlank(message = "显示顺序不能为空") 31 @NotBlank(message = "显示顺序不能为空")
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/SysMenuListReqVO.java
@@ -14,7 +14,7 @@ public class SysMenuListReqVO { @@ -14,7 +14,7 @@ public class SysMenuListReqVO {
14 @ApiModelProperty(value = "菜单名称", example = "芋道", notes = "模糊匹配") 14 @ApiModelProperty(value = "菜单名称", example = "芋道", notes = "模糊匹配")
15 private String menuName; 15 private String menuName;
16 16
17 - @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 CommonStatusEnum 枚举类") 17 + @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
18 private Integer status; 18 private Integer status;
19 19
20 } 20 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/SysMenuRespVO.java
@@ -17,9 +17,9 @@ import java.util.Date; @@ -17,9 +17,9 @@ import java.util.Date;
17 public class SysMenuRespVO extends SysMenuBaseVO { 17 public class SysMenuRespVO extends SysMenuBaseVO {
18 18
19 @ApiModelProperty(value = "菜单编号", required = true, example = "1024") 19 @ApiModelProperty(value = "菜单编号", required = true, example = "1024")
20 - private Integer menuId; 20 + private Integer id;
21 21
22 - @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举类") 22 + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
23 private Integer status; 23 private Integer status;
24 24
25 @ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式") 25 @ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/SysMenuUpdateReqVO.java
@@ -13,6 +13,6 @@ public class SysMenuUpdateReqVO extends SysMenuBaseVO { @@ -13,6 +13,6 @@ public class SysMenuUpdateReqVO extends SysMenuBaseVO {
13 13
14 @ApiModelProperty(value = "菜单编号", required = true, example = "1024") 14 @ApiModelProperty(value = "菜单编号", required = true, example = "1024")
15 @NotNull(message = "菜单编号不能为空") 15 @NotNull(message = "菜单编号不能为空")
16 - private Integer menuId; 16 + private Long id;
17 17
18 } 18 }
src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
1 package cn.iocoder.dashboard.modules.system.convert.auth; 1 package cn.iocoder.dashboard.modules.system.convert.auth;
2 2
3 import cn.iocoder.dashboard.framework.security.core.LoginUser; 3 import cn.iocoder.dashboard.framework.security.core.LoginUser;
4 -import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthPermissionInfoRespVO;  
5 import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthMenuRespVO; 4 import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthMenuRespVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthPermissionInfoRespVO;
6 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 6 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; 7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; 8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
  9 +import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum;
9 import cn.iocoder.dashboard.util.collection.CollectionUtils; 10 import cn.iocoder.dashboard.util.collection.CollectionUtils;
10 import org.mapstruct.Mapper; 11 import org.mapstruct.Mapper;
11 import org.mapstruct.Mapping; 12 import org.mapstruct.Mapping;
12 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
  14 +import org.slf4j.LoggerFactory;
13 15
14 -import java.util.List; 16 +import java.util.*;
15 17
16 @Mapper 18 @Mapper
17 public interface SysAuthConvert { 19 public interface SysAuthConvert {
@@ -31,4 +33,36 @@ public interface SysAuthConvert { @@ -31,4 +33,36 @@ public interface SysAuthConvert {
31 33
32 SysAuthMenuRespVO convertTreeNode(SysMenuDO menu); 34 SysAuthMenuRespVO convertTreeNode(SysMenuDO menu);
33 35
  36 + /**
  37 + * 将菜单列表,构建成菜单树
  38 + *
  39 + * @param menuList 菜单列表
  40 + * @return 菜单树
  41 + */
  42 + default List<SysAuthMenuRespVO> buildMenuTree(List<SysMenuDO> menuList) {
  43 + // 排序,保证菜单的有序性
  44 + menuList.sort(Comparator.comparing(SysMenuDO::getSort));
  45 + // 构建菜单树
  46 + // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
  47 + Map<Long, SysAuthMenuRespVO> treeNodeMap = new LinkedHashMap<>();
  48 + menuList.forEach(menu -> treeNodeMap.put(menu.getId(), SysAuthConvert.INSTANCE.convertTreeNode(menu)));
  49 + // 处理父子关系
  50 + treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach((childNode) -> {
  51 + // 获得父节点
  52 + SysAuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId());
  53 + if (parentNode == null) {
  54 + LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
  55 + childNode.getId(), childNode.getParentId());
  56 + return;
  57 + }
  58 + // 将自己添加到父节点中
  59 + if (parentNode.getChildren() == null) {
  60 + parentNode.setChildren(new ArrayList<>());
  61 + }
  62 + parentNode.getChildren().add(childNode);
  63 + });
  64 + // 获得到所有的根节点
  65 + return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId()));
  66 + }
  67 +
34 } 68 }
src/main/java/cn/iocoder/dashboard/modules/system/convert/permission/SysMenuConvert.java
1 package cn.iocoder.dashboard.modules.system.convert.permission; 1 package cn.iocoder.dashboard.modules.system.convert.permission;
2 2
  3 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
3 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO; 4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
4 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 6 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
5 import org.mapstruct.Mapper; 7 import org.mapstruct.Mapper;
6 import org.mapstruct.factory.Mappers; 8 import org.mapstruct.factory.Mappers;
@@ -14,4 +16,10 @@ public interface SysMenuConvert { @@ -14,4 +16,10 @@ public interface SysMenuConvert {
14 16
15 List<SysMenuRespVO> convertList(List<SysMenuDO> list); 17 List<SysMenuRespVO> convertList(List<SysMenuDO> list);
16 18
  19 + SysMenuDO convert(SysMenuCreateReqVO bean);
  20 +
  21 + SysMenuDO convert(SysMenuUpdateReqVO bean);
  22 +
  23 + SysMenuRespVO convert(SysMenuDO bean);
  24 +
17 } 25 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
2 2
3 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 3 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
  4 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
6 7
7 @Mapper 8 @Mapper
8 public interface SysMenuMapper extends BaseMapper<SysMenuDO> { 9 public interface SysMenuMapper extends BaseMapper<SysMenuDO> {
  10 +
  11 + default SysMenuDO selectByParentIdAndName(Long parentId, String name) {
  12 + return selectOne(new QueryWrapper<SysMenuDO>().eq("parent_id", parentId)
  13 + .eq("name", name));
  14 + }
  15 +
  16 + default Integer selectCountByParentId(Long parentId) {
  17 + return selectCount(new QueryWrapper<SysMenuDO>().eq("parent_id", parentId));
  18 + }
  19 +
9 } 20 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dict/SysDictDataDO.java
@@ -26,30 +26,30 @@ public class SysDictDataDO extends BaseDO { @@ -26,30 +26,30 @@ public class SysDictDataDO extends BaseDO {
26 */ 26 */
27 @TableId 27 @TableId
28 @Excel(name = "字典编码", cellType = Excel.ColumnType.NUMERIC) 28 @Excel(name = "字典编码", cellType = Excel.ColumnType.NUMERIC)
29 - private Long dictCode; 29 + private Long id;
30 /** 30 /**
31 * 字典排序 31 * 字典排序
32 */ 32 */
33 @Excel(name = "字典排序", cellType = Excel.ColumnType.NUMERIC) 33 @Excel(name = "字典排序", cellType = Excel.ColumnType.NUMERIC)
34 - private Integer dictSort; 34 + private Integer sort;
35 /** 35 /**
36 * 字典标签 36 * 字典标签
37 */ 37 */
38 @Excel(name = "字典标签") 38 @Excel(name = "字典标签")
39 @NotBlank(message = "字典标签不能为空") 39 @NotBlank(message = "字典标签不能为空")
40 @Size(max = 100, message = "字典标签长度不能超过100个字符") 40 @Size(max = 100, message = "字典标签长度不能超过100个字符")
41 - private String dictLabel; 41 + private String label;
42 /** 42 /**
43 - * 字典键值 43 + * 字典值
44 */ 44 */
45 @Excel(name = "字典键值") 45 @Excel(name = "字典键值")
46 @NotBlank(message = "字典键值不能为空") 46 @NotBlank(message = "字典键值不能为空")
47 @Size(max = 100, message = "字典键值长度不能超过100个字符") 47 @Size(max = 100, message = "字典键值长度不能超过100个字符")
48 - private String dictValue; 48 + private String value;
49 /** 49 /**
50 * 字典类型 50 * 字典类型
51 * 51 *
52 - * 外键 {@link SysDictDataDO#getDictType()} 52 + * 冗余 {@link SysDictDataDO#getDictType()}
53 */ 53 */
54 @Excel(name = "字典类型") 54 @Excel(name = "字典类型")
55 @NotBlank(message = "字典类型不能为空") 55 @NotBlank(message = "字典类型不能为空")
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dict/SysDictTypeDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dict; @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dict;
3 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 3 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
4 import cn.iocoder.dashboard.framework.excel.Excel; 4 import cn.iocoder.dashboard.framework.excel.Excel;
5 import cn.iocoder.dashboard.framework.mybatis.core.BaseDO; 5 import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
  6 +import com.baomidou.mybatisplus.annotation.TableField;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Data; 9 import lombok.Data;
@@ -26,24 +27,22 @@ public class SysDictTypeDO extends BaseDO { @@ -26,24 +27,22 @@ public class SysDictTypeDO extends BaseDO {
26 */ 27 */
27 @TableId 28 @TableId
28 @Excel(name = "字典主键", cellType = Excel.ColumnType.NUMERIC) 29 @Excel(name = "字典主键", cellType = Excel.ColumnType.NUMERIC)
29 - private Long dictId;  
30 - 30 + private Long id;
31 /** 31 /**
32 * 字典名称 32 * 字典名称
33 */ 33 */
34 @Excel(name = "字典名称") 34 @Excel(name = "字典名称")
35 @NotBlank(message = "字典名称不能为空") 35 @NotBlank(message = "字典名称不能为空")
36 @Size(max = 100, message = "字典类型名称长度不能超过100个字符") 36 @Size(max = 100, message = "字典类型名称长度不能超过100个字符")
37 - private String dictName;  
38 - 37 + private String name;
39 /** 38 /**
40 * 字典类型 39 * 字典类型
41 */ 40 */
  41 + @TableField("dict_type")
42 @Excel(name = "字典类型") 42 @Excel(name = "字典类型")
43 @NotBlank(message = "字典类型不能为空") 43 @NotBlank(message = "字典类型不能为空")
44 @Size(max = 100, message = "字典类型类型长度不能超过100个字符") 44 @Size(max = 100, message = "字典类型类型长度不能超过100个字符")
45 - private String dictType;  
46 - 45 + private String type;
47 /** 46 /**
48 * 状态 47 * 状态
49 * 48 *
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission; @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
3 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 3 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
4 import cn.iocoder.dashboard.framework.mybatis.core.BaseDO; 4 import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
5 import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; 5 import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
  6 +import com.baomidou.mybatisplus.annotation.TableField;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Data; 9 import lombok.Data;
@@ -22,11 +23,11 @@ public class SysMenuDO extends BaseDO { @@ -22,11 +23,11 @@ public class SysMenuDO extends BaseDO {
22 * 菜单ID 23 * 菜单ID
23 */ 24 */
24 @TableId 25 @TableId
25 - private Long menuId; 26 + private Long id;
26 /** 27 /**
27 * 菜单名称 28 * 菜单名称
28 */ 29 */
29 - private String menuName; 30 + private String name;
30 /** 31 /**
31 * 权限标识 32 * 权限标识
32 * 33 *
@@ -43,7 +44,8 @@ public class SysMenuDO extends BaseDO { @@ -43,7 +44,8 @@ public class SysMenuDO extends BaseDO {
43 * 44 *
44 * 枚举 {@link MenuTypeEnum} 45 * 枚举 {@link MenuTypeEnum}
45 */ 46 */
46 - private Integer menuType; 47 + @TableField("menu_type")
  48 + private Integer type;
47 /** 49 /**
48 * 显示顺序 50 * 显示顺序
49 */ 51 */
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -18,4 +18,12 @@ public interface SysErrorCodeConstants { @@ -18,4 +18,12 @@ public interface SysErrorCodeConstants {
18 ErrorCode TOKEN_EXPIRED = new ErrorCode(1002001000, "Token 已经过期"); 18 ErrorCode TOKEN_EXPIRED = new ErrorCode(1002001000, "Token 已经过期");
19 ErrorCode TOKEN_PARSE_FAIL = new ErrorCode(1002001001, "Token 解析失败"); 19 ErrorCode TOKEN_PARSE_FAIL = new ErrorCode(1002001001, "Token 解析失败");
20 20
  21 + // ========== 菜单模块 1002002000 ==========
  22 + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002002000, "已经存在该名字的菜单");
  23 + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1002002001, "父菜单不存在");
  24 + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1002002002, "不能设置自己为父菜单");
  25 + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1002002003, "菜单不存在");
  26 + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1002002004, "存在子菜单,无法删除");
  27 + ErrorCode MENU_PARENT_NOT_MENU = new ErrorCode(1002002005, "父菜单的类型必须是菜单");
  28 +
21 } 29 }
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -12,7 +12,6 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMe @@ -12,7 +12,6 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMe
12 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; 12 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
13 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; 13 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
14 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO; 14 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
15 -import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum;  
16 import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; 15 import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
17 import cn.iocoder.dashboard.modules.system.enums.user.UserStatus; 16 import cn.iocoder.dashboard.modules.system.enums.user.UserStatus;
18 import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; 17 import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
@@ -38,7 +37,10 @@ import org.springframework.stereotype.Service; @@ -38,7 +37,10 @@ import org.springframework.stereotype.Service;
38 import org.springframework.util.Assert; 37 import org.springframework.util.Assert;
39 38
40 import javax.annotation.Resource; 39 import javax.annotation.Resource;
41 -import java.util.*; 40 +import java.util.Date;
  41 +import java.util.HashSet;
  42 +import java.util.List;
  43 +import java.util.Set;
42 44
43 import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; 45 import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
44 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; 46 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -243,36 +245,12 @@ public class SysAuthServiceImpl implements SysAuthService { @@ -243,36 +245,12 @@ public class SysAuthServiceImpl implements SysAuthService {
243 245
244 @Override 246 @Override
245 public List<SysAuthMenuRespVO> listMenus(Long userId, Set<Long> roleIds) { 247 public List<SysAuthMenuRespVO> listMenus(Long userId, Set<Long> roleIds) {
  248 + // 获得用户拥有的菜单列表
246 List<SysMenuDO> menuList = permissionService.listRoleMenusFromCache(roleIds, 249 List<SysMenuDO> menuList = permissionService.listRoleMenusFromCache(roleIds,
247 - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()),  
248 - SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); 250 + SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
  251 + SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
249 // 转换成 Tree 结构返回 252 // 转换成 Tree 结构返回
250 - return buildRouterTree(menuList);  
251 - }  
252 -  
253 - private static List<SysAuthMenuRespVO> buildRouterTree(List<SysMenuDO> menuList) {  
254 - // 排序,保证菜单的有序性  
255 - menuList.sort(Comparator.comparing(SysMenuDO::getSort));  
256 - // 构建菜单树  
257 - // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。  
258 - Map<Long, SysAuthMenuRespVO> treeNodeMap = new LinkedHashMap<>();  
259 - menuList.forEach(menu -> treeNodeMap.put(menu.getMenuId(), SysAuthConvert.INSTANCE.convertTreeNode(menu)));  
260 - // 处理父子关系  
261 - treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach((childNode) -> {  
262 - // 获得父节点  
263 - SysAuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId());  
264 - if (parentNode == null) {  
265 - log.error("[buildRouterTree][resource({}) 找不到父资源({})]", childNode.getMenuId(), childNode.getParentId());  
266 - return;  
267 - }  
268 - // 将自己添加到父节点中  
269 - if (parentNode.getChildren() == null) {  
270 - parentNode.setChildren(new ArrayList<>());  
271 - }  
272 - parentNode.getChildren().add(childNode);  
273 - });  
274 - // 获得到所有的根节点  
275 - return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId())); 253 + return SysAuthConvert.INSTANCE.buildMenuTree(menuList);
276 } 254 }
277 255
278 } 256 }
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java
@@ -19,7 +19,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { @@ -19,7 +19,7 @@ public class SysDictDataServiceImpl implements SysDictDataService {
19 19
20 private static final Comparator<SysDictDataDO> COMPARATOR_TYPE_AND_SORT = Comparator 20 private static final Comparator<SysDictDataDO> COMPARATOR_TYPE_AND_SORT = Comparator
21 .comparing(SysDictDataDO::getDictType) 21 .comparing(SysDictDataDO::getDictType)
22 - .thenComparingInt(SysDictDataDO::getDictSort); 22 + .thenComparingInt(SysDictDataDO::getSort);
23 23
24 @Resource 24 @Resource
25 private SysDictDataMapper dictDataMapper; 25 private SysDictDataMapper dictDataMapper;
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java
1 package cn.iocoder.dashboard.modules.system.service.permission; 1 package cn.iocoder.dashboard.modules.system.service.permission;
2 2
  3 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
3 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO; 4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO;
4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO; 5 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
  6 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
5 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
6 8
7 import java.util.Collection; 9 import java.util.Collection;
@@ -9,6 +11,8 @@ import java.util.List; @@ -9,6 +11,8 @@ import java.util.List;
9 11
10 /** 12 /**
11 * 菜单 Service 接口 13 * 菜单 Service 接口
  14 + *
  15 + * @author 芋道源码
12 */ 16 */
13 public interface SysMenuService { 17 public interface SysMenuService {
14 18
@@ -49,4 +53,34 @@ public interface SysMenuService { @@ -49,4 +53,34 @@ public interface SysMenuService {
49 List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes, 53 List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
50 Collection<Integer> menusStatuses); 54 Collection<Integer> menusStatuses);
51 55
  56 + /*
  57 + * 创建菜单
  58 + *
  59 + * @param reqVO 菜单信息
  60 + * @return 创建出来的菜单编号
  61 + */
  62 + Long createMenu(SysMenuCreateReqVO reqVO);
  63 +
  64 + /**
  65 + * 更新菜单
  66 + *
  67 + * @param reqVO 菜单信息
  68 + */
  69 + void updateMenu(SysMenuUpdateReqVO reqVO);
  70 +
  71 + /**
  72 + * 删除菜单
  73 + *
  74 + * @param id 菜单编号
  75 + */
  76 + void deleteMenu(Long id);
  77 +
  78 + /**
  79 + * 获得菜单
  80 + *
  81 + * @param id 菜单编号
  82 + * @return 菜单
  83 + */
  84 + SysMenuDO getMenu(Long id);
  85 +
52 } 86 }
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
@@ -48,4 +48,11 @@ public interface SysPermissionService { @@ -48,4 +48,11 @@ public interface SysPermissionService {
48 */ 48 */
49 Long getDeptRoleId(Long deptId); 49 Long getDeptRoleId(Long deptId);
50 50
  51 + /**
  52 + * 删除授予给角色的菜单们
  53 + *
  54 + * @param menuId 菜单编号
  55 + */
  56 + void deleteRolesMenuByMenuId(Long menuId);
  57 +
51 } 58 }
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
1 package cn.iocoder.dashboard.modules.system.service.permission.impl; 1 package cn.iocoder.dashboard.modules.system.service.permission.impl;
2 2
  3 +import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
  4 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
3 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO; 5 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO;
4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO; 6 import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
5 import cn.iocoder.dashboard.modules.system.convert.permission.SysMenuConvert; 8 import cn.iocoder.dashboard.modules.system.convert.permission.SysMenuConvert;
6 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMapper; 9 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMapper;
7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 10 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
  11 +import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum;
  12 +import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
8 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; 13 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
  14 +import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
9 import cn.iocoder.dashboard.util.collection.CollectionUtils; 15 import cn.iocoder.dashboard.util.collection.CollectionUtils;
10 import com.google.common.collect.ImmutableMap; 16 import com.google.common.collect.ImmutableMap;
11 import com.google.common.collect.ImmutableMultimap; 17 import com.google.common.collect.ImmutableMultimap;
@@ -21,8 +27,12 @@ import java.util.List; @@ -21,8 +27,12 @@ import java.util.List;
21 import java.util.Map; 27 import java.util.Map;
22 import java.util.stream.Collectors; 28 import java.util.stream.Collectors;
23 29
  30 +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
  31 +
24 /** 32 /**
25 * 菜单 Service 实现 33 * 菜单 Service 实现
  34 + *
  35 + * @author 芋道源码
26 */ 36 */
27 @Service 37 @Service
28 @Slf4j 38 @Slf4j
@@ -46,6 +56,8 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -46,6 +56,8 @@ public class SysMenuServiceImpl implements SysMenuService {
46 56
47 @Resource 57 @Resource
48 private SysMenuMapper menuMapper; 58 private SysMenuMapper menuMapper;
  59 + @Resource
  60 + private SysPermissionService permissionService;
49 61
50 /** 62 /**
51 * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存 63 * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存
@@ -57,7 +69,7 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -57,7 +69,7 @@ public class SysMenuServiceImpl implements SysMenuService {
57 ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder(); 69 ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
58 ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder(); 70 ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
59 menuList.forEach(menuDO -> { 71 menuList.forEach(menuDO -> {
60 - menuCacheBuilder.put(menuDO.getMenuId(), menuDO); 72 + menuCacheBuilder.put(menuDO.getId(), menuDO);
61 permMenuCacheBuilder.put(menuDO.getPermission(), menuDO); 73 permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
62 }); 74 });
63 menuCache = menuCacheBuilder.build(); 75 menuCache = menuCacheBuilder.build();
@@ -79,7 +91,7 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -79,7 +91,7 @@ public class SysMenuServiceImpl implements SysMenuService {
79 return Collections.emptyList(); 91 return Collections.emptyList();
80 } 92 }
81 // 创建新数组,避免缓存被修改 93 // 创建新数组,避免缓存被修改
82 - return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getMenuType()) 94 + return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getType())
83 && menusStatuses.contains(menu.getStatus())) 95 && menusStatuses.contains(menu.getStatus()))
84 .collect(Collectors.toList()); 96 .collect(Collectors.toList());
85 } 97 }
@@ -91,10 +103,186 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -91,10 +103,186 @@ public class SysMenuServiceImpl implements SysMenuService {
91 if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) { 103 if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
92 return Collections.emptyList(); 104 return Collections.emptyList();
93 } 105 }
94 - return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getMenuId())  
95 - && menuTypes.contains(menu.getMenuType()) 106 + return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getId())
  107 + && menuTypes.contains(menu.getType())
96 && menusStatuses.contains(menu.getStatus())) 108 && menusStatuses.contains(menu.getStatus()))
97 .collect(Collectors.toList()); 109 .collect(Collectors.toList());
98 } 110 }
99 111
  112 + @Override
  113 + public Long createMenu(SysMenuCreateReqVO reqVO) {
  114 + // 校验父菜单存在
  115 + checkParentResource(reqVO.getParentId(), null);
  116 + // 校验菜单(自己)
  117 + checkResource(reqVO.getParentId(), reqVO.getName(), null);
  118 + // 插入数据库
  119 + SysMenuDO menu = SysMenuConvert.INSTANCE.convert(reqVO);
  120 + initMenuProperty(menu);
  121 + menuMapper.insert(menu);
  122 + // 返回
  123 + return menu.getId();
  124 + }
  125 +
  126 + @Override
  127 + public void updateMenu(SysMenuUpdateReqVO reqVO) {
  128 + // 校验更新的菜单是否存在
  129 + if (menuMapper.selectById(reqVO.getId()) == null) {
  130 + throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
  131 + }
  132 + // 校验父菜单存在
  133 + checkParentResource(reqVO.getParentId(), reqVO.getId());
  134 + // 校验菜单(自己)
  135 + checkResource(reqVO.getParentId(), reqVO.getName(), reqVO.getId());
  136 + // 更新到数据库
  137 + SysMenuDO updateObject = SysMenuConvert.INSTANCE.convert(reqVO);
  138 + initMenuProperty(updateObject);
  139 + menuMapper.updateById(updateObject);
  140 + }
  141 +
  142 + /**
  143 + * 删除菜单
  144 + *
  145 + * @param menuId 菜单编号
  146 + */
  147 + public void deleteMenu(Long menuId) {
  148 + // 校验更新的菜单是否存在
  149 + if (menuMapper.selectById(menuId) == null) {
  150 + throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
  151 + }
  152 + // 校验是否还有子菜单
  153 + if (menuMapper.selectCountByParentId(menuId) > 0) {
  154 + throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN);
  155 + }
  156 + // 校验删除的菜单是否存在
  157 + if (menuMapper.selectById(menuId) == null) {
  158 + throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
  159 + }
  160 + // 标记删除
  161 + menuMapper.deleteById(menuId);
  162 + // 删除授予给角色的权限
  163 + permissionService.deleteRolesMenuByMenuId(menuId);
  164 + }
  165 +
  166 + @Override
  167 + public SysMenuDO getMenu(Long id) {
  168 + return menuMapper.selectById(id);
  169 + }
  170 +
  171 +// /**
  172 +// * 获得菜单列表
  173 +// *
  174 +// * @param menuIds 菜单编号列表
  175 +// * @return 菜单列表
  176 +// */
  177 +// public List<ResourceBO> listResources(List<Integer> menuIds) {
  178 +// List<ResourceDO> menuDOs = menuMapper.selectBatchIds(menuIds);
  179 +// return ResourceConvert.INSTANCE.convertList(menuDOs);
  180 +// }
  181 +//
  182 +// /**
  183 +// * 获得菜单全列表
  184 +// *
  185 +// * @return 菜单全列表
  186 +// */
  187 +// public List<ResourceBO> listResources() {
  188 +// List<ResourceDO> menuDOs = menuMapper.selectList(null);
  189 +// return ResourceConvert.INSTANCE.convertList(menuDOs);
  190 +// }
  191 +//
  192 +// /**
  193 +// * 获得指定类型的菜单列表
  194 +// *
  195 +// * @param type 菜单类型,允许空
  196 +// * @return 菜单列表
  197 +// */
  198 +// public List<ResourceBO> listResourcesByType(Integer type) {
  199 +// List<ResourceDO> menuDOs = menuMapper.selectListByType(type);
  200 +// return ResourceConvert.INSTANCE.convertList(menuDOs);
  201 +// }
  202 +//
  203 +// /**
  204 +// * 获得角色拥有的菜单列表
  205 +// *
  206 +// * @param roleIds 角色编号
  207 +// * @param type 菜单类型,允许空
  208 +// * @return 菜单列表
  209 +// */
  210 +// public List<ResourceBO> listRoleResourcesByType(Collection<Integer> roleIds, Integer type) {
  211 +// List<RoleResourceDO> roleResourceDOs = roleResourceMapper.selectListByRoleIds(roleIds);
  212 +// if (CollectionUtils.isEmpty(roleResourceDOs)) {
  213 +// return Collections.emptyList();
  214 +// }
  215 +// List<ResourceDO> menuDOs = menuMapper.selectListByIdsAndType(
  216 +// CollectionUtils.convertSet(roleResourceDOs, RoleResourceDO::getResourceId), type);
  217 +// return ResourceConvert.INSTANCE.convertList(menuDOs);
  218 +// }
  219 +//
  220 + /**
  221 + * 校验父菜单是否合法
  222 + *
  223 + * 1. 不能设置自己为父菜单
  224 + * 2. 父菜单不存在
  225 + * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型
  226 + *
  227 + * @param parentId 父菜单编号
  228 + * @param childId 当前菜单编号
  229 + */
  230 + private void checkParentResource(Long parentId, Long childId) {
  231 + if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) {
  232 + return;
  233 + }
  234 + // 不能设置自己为父菜单
  235 + if (parentId.equals(childId)) {
  236 + throw ServiceExceptionUtil.exception(MENU_PARENT_ERROR);
  237 + }
  238 + SysMenuDO menu = menuMapper.selectById(parentId);
  239 + // 父菜单不存在
  240 + if (menu == null) {
  241 + throw ServiceExceptionUtil.exception(MENU_PARENT_NOT_EXISTS);
  242 + }
  243 + // 父菜单必须是目录类型
  244 + if (!MenuTypeEnum.DIR.getType().equals(menu.getType())) {
  245 + throw ServiceExceptionUtil.exception(MENU_PARENT_NOT_MENU);
  246 + }
  247 + }
  248 +
  249 + /**
  250 + * 校验菜单是否合法
  251 + *
  252 + * 1. 校验相同父菜单编号下,是否存在相同的菜单名
  253 + *
  254 + * @param name 菜单名字
  255 + * @param parentId 父菜单编号
  256 + * @param id 菜单编号
  257 + */
  258 + private void checkResource(Long parentId, String name, Long id) {
  259 + SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name);
  260 + if (menu == null) {
  261 + return;
  262 + }
  263 + // 如果 menuId 为空,说明不用比较是否为相同 menuId 的菜单
  264 + if (id == null) {
  265 + throw ServiceExceptionUtil.exception(MENU_NAME_DUPLICATE);
  266 + }
  267 + if (!menu.getId().equals(id)) {
  268 + throw ServiceExceptionUtil.exception(MENU_NAME_DUPLICATE);
  269 + }
  270 + }
  271 +
  272 + /**
  273 + * 初始化菜单的通用属性。
  274 + *
  275 + * 例如说,只有目录或者菜单类型的菜单,才设置 icon
  276 + *
  277 + * @param menu 菜单
  278 + */
  279 + private void initMenuProperty(SysMenuDO menu) {
  280 + // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空
  281 + if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) {
  282 + menu.setComponent("");
  283 + menu.setIcon("");
  284 + menu.setPath("");
  285 + }
  286 + }
  287 +
100 } 288 }
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
@@ -107,4 +107,9 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -107,4 +107,9 @@ public class SysPermissionServiceImpl implements SysPermissionService {
107 return roleDept != null ? roleDept.getRoleId() : null; 107 return roleDept != null ? roleDept.getRoleId() : null;
108 } 108 }
109 109
  110 + @Override
  111 + public void deleteRolesMenuByMenuId(Long menuId) {
  112 + // TODO 实现我
  113 + }
  114 +
110 } 115 }