博客后台模块续更(五)
十一、后台模块-菜单列表
菜单指的是权限菜单,也就是一堆权限字符串
1. 查询菜单
1.1 接口分析
需要展示菜单列表,不需要分页。可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询。菜单要按照父菜单id和orderNum进行排序
请求方式 | 请求路径 | 是否需求token头 |
GET | system/menu/list | 是 |
请求参数是query格式的:
{
status : 状态menuName: 菜单名
}
响应格式:
{"code":200,"data":[{"component":"组件路径","icon":"build","id":"2023","isFrame":1,"menuName":"菜单名称","menuType":"C","orderNum":0,"parentId":"0","path":"write","perms":"权限字符串","remark":"备注信息","status":"0","visible":"0"},{"icon":"system","id":"1","isFrame":1,"menuName":"菜单名称","menuType":"M","orderNum":1,"parentId":"0","path":"system","perms":"权限字符串","remark":"备注信息","status":"0","visible":"0"}],"msg":"操作成功"
}
2.2 代码实现
第一步: 在keke-framework工程的Vo目录新建MenuVo类,写入如下,用于把指定字段返回给前端
package com.keke.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminMenuVo {//菜单IDprivate Long id;//菜单名称private String menuName;//父菜单IDprivate Long parentId;//显示顺序private Integer orderNum;//路由地址private String path;//组件路径private String component;//是否为外链(0是 1否)private Integer isFrame;//菜单类型(M目录 C菜单 F按钮)private String menuType;//菜单状态(0显示 1隐藏)private String visible;//菜单状态(0正常 1停用)private String status;//权限标识private String perms;//菜单图标private String icon;//备注private String remark;
}
第二步: 在keke-admin工程的controller目录新建MenuController类,写入如下,是查询菜单列表的访问接口
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/system/menu")
public class MenuController {@Autowiredprivate MenuService menuService;//查询菜单列表@GetMapping("/list")public ResponseResult selectAllMenu(Menu menu){return menuService.selectAllMenu(menu);}
}
第三步:把keke-framework工程的MenuService接口修改为如下,增加了查询菜单列表的接口
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;import java.util.List;/*** 菜单权限表(Menu)表服务接口** @author makejava* @since 2023-10-18 20:55:48*/
public interface MenuService extends IService<Menu> {//查询用户权限信息List<String> selectPermsByUserId(Long userId);//查询用户的路由信息,也就是权限菜单List<Menu> selectRouterMenuTreeByUserId(Long userId);ResponseResult selectAllMenu(Menu menu);}
第四步: 把keke-framework工程的MenuServiceImpl类修改为如下,增加了查询菜单列表的具体代码实现
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 菜单权限表(Menu)表服务实现类** @author makejava* @since 2023-10-18 20:55:48*/
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {//根据用户id查询权限关键字@Overridepublic List<String> selectPermsByUserId(Long userId) {//如果用户id为1代表管理员,roles 中只需要有admin,// permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限if(SecurityUtils.isAdmin()) {LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);//由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapperList<Menu> menuList = list(lambdaQueryWrapper);//我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式List<String> permissions = menuList.stream().map(new Function<Menu, String>() {@Overridepublic String apply(Menu menu) {String perms = menu.getPerms();return perms;}}).collect(Collectors.toList());return permissions;}//否则返回这个用户所具有的权限//这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联MenuMapper menuMapper = getBaseMapper();//我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回return menuMapper.selectPermsByUserId(userId);}@Overridepublic List<Menu> selectRouterMenuTreeByUserId(Long userId) {MenuMapper menuMapper = getBaseMapper();List<Menu> menus = null;//如果是管理员,返回所有if(SecurityUtils.isAdmin()){menus = menuMapper.selectAllRoutersMenu();}else {//如果不是管理员,返回对应用户的菜单menus = menuMapper.selectRoutersMenuTreeByUserId(userId);}//因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建TreeList<Menu> menuTree = buildMenuTree(menus,0L);return menuTree;}@Overridepublic ResponseResult selectAllMenu(Menu menu) {//可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());//排序 parent_id和order_numlambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);List<Menu> menus = list(lambdaQueryWrapper);List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);return ResponseResult.okResult(adminMenuVos);}/*** 构建MenuTree* 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children* @param menus* @return*/private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {//转化流处理List<Menu> menuTree = menus.stream()//过滤掉除一级菜单之外的菜单.filter(menu -> menu.getParentId().equals(parentId))//然后将获取其子菜单设置到children字段,并返回.map(m -> m.setChildren(gerChildren(m, menus))).collect(Collectors.toList());return menuTree;}//获取当前菜单的子菜单private List<Menu> gerChildren(Menu menu, List<Menu> menus) {//流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤List<Menu> children = menus.stream().filter(m -> m.getParentId().equals(menu.getId()))//这里其实不必要写,这一步的逻辑是如果有三级,//可以把流对象中再过筛选出子菜单设置给对应的children并返回.map(m -> m.setChildren(gerChildren(m,menus))).collect(Collectors.toList());return children;}}
2.3 测试
运行前端工程,打开redis,打开菜单管理
2. 新增菜单
2.1 接口分析
新增权限菜单
请求方式 | 请求路径 | 是否需求token头 |
POST | system/menu | 是 |
请求体参数:
Menu类对应的json格式
响应格式:
{"code":200,"msg":"操作成功"
}
2.2 代码实现
第一步: 把keke-framework工程的Menu类修改为如下,注意有四个字段使用了mybatisplus的字段自增
package com.keke.domain.entity;import java.util.Date;
import java.util.List;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.experimental.Accessors;/*** 菜单权限表(Menu)表实体类** @author makejava* @since 2023-10-18 20:55:24*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_menu")
public class Menu {//菜单IDprivate Long id;//菜单名称private String menuName;//父菜单IDprivate Long parentId;//显示顺序private Integer orderNum;//路由地址private String path;//组件路径private String component;//是否为外链(0是 1否)private Integer isFrame;//菜单类型(M目录 C菜单 F按钮)private String menuType;//菜单状态(0显示 1隐藏)private String visible;//菜单状态(0正常 1停用)private String status;//权限标识private String perms;//菜单图标private String icon;//创建者@TableField(fill = FieldFill.INSERT)private Long createBy;//创建时间@TableField(fill = FieldFill.INSERT)private Date createTime;//更新者@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateBy;//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;//备注private String remark;private String delFlag;//由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解// 让mybatis在查表时不查询这个字段@TableField(exist = false)private List<Menu> children;
}
第二步: 把huanf-framework工程的MenuController类修改为如下,增加了新增菜单的具体代码实现
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/menu")
public class MenuController {@Autowiredprivate MenuService menuService;//查询菜单列表@GetMapping("/list")public ResponseResult selectAllMenu(Menu menu){return menuService.selectAllMenu(menu);}//新增菜单@PostMappingpublic ResponseResult add(@RequestBody Menu menu) {menuService.save(menu);return ResponseResult.okResult();}}
2.3 测试
启动工程,打开前端工程,redis
测试在 '系统管理' 页面,点击 '新增',能否可以添加"测试目录"类型的菜单
3. 修改菜单
能够修改菜单,但是修改的时候不能把父菜单设置为当前菜单,如果设置了需要给出相应的提示。并且修改失败。
修改菜单包含两个接口,一个是点击修改回显出菜单的详情信息,一个是点击确定后菜单修改成功
3.1 菜单详情
3.1.1菜单详情接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
Get | system/menu/{id} | 是 |
请求参数PathVariable格式:
id: 菜单id
响应格式:
{"code":200,"data":{"icon":"table","id":"2017","menuName":"内容管理","menuType":"M","orderNum":"4","parentId":"0","path":"content","remark":"","status":"0","visible":"0"},"msg":"操作成功"
}
3.1.2 代码实现
controller层新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/menu")
public class MenuController {@Autowiredprivate MenuService menuService;//查询菜单列表@GetMapping("/list")public ResponseResult selectAllMenu(Menu menu){return menuService.selectAllMenu(menu);}//新增菜单@PostMappingpublic ResponseResult add(@RequestBody Menu menu) {menuService.save(menu);return ResponseResult.okResult();}@GetMapping("/{menuId}")public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){Menu menu = menuService.getById(menuId);return ResponseResult.okResult(menu);}}
3.1.3 测试
点击修改按钮后,回显出菜单的详情信息
3.2 修改菜单
3.2.1 修改菜单接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | system/menu | 是 |
请求体参数:
Menu类对应的json格式
响应格式:
{"code":200,"msg":"操作成功"
}
如果把父菜单设置为当前菜单:
{"code":500,"msg":"修改菜单'写博文'失败,上级菜单不能选择自己"
}
3.2.2 代码实现
第一步:Controller层新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/menu")
public class MenuController {@Autowiredprivate MenuService menuService;//查询菜单列表@GetMapping("/list")public ResponseResult selectAllMenu(Menu menu){return menuService.selectAllMenu(menu);}//新增菜单@PostMappingpublic ResponseResult add(@RequestBody Menu menu) {menuService.save(menu);return ResponseResult.okResult();}@GetMapping("/{menuId}")public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){Menu menu = menuService.getById(menuId);return ResponseResult.okResult(menu);}@PutMappingpublic ResponseResult editMenu(@RequestBody Menu menu){return menuService.editMenu(menu);}}
第二步:domain/entity的Menu实体类修改四个字段,设置为mp自动填充
package com.keke.domain.entity;import java.util.Date;
import java.util.List;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.experimental.Accessors;/*** 菜单权限表(Menu)表实体类** @author makejava* @since 2023-10-18 20:55:24*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_menu")
public class Menu {//菜单IDprivate Long id;//菜单名称private String menuName;//父菜单IDprivate Long parentId;//显示顺序private Integer orderNum;//路由地址private String path;//组件路径private String component;//是否为外链(0是 1否)private Integer isFrame;//菜单类型(M目录 C菜单 F按钮)private String menuType;//菜单状态(0显示 1隐藏)private String visible;//菜单状态(0正常 1停用)private String status;//权限标识private String perms;//菜单图标private String icon;//创建者@TableField(fill = FieldFill.INSERT)private Long createBy;//创建时间@TableField(fill = FieldFill.INSERT)private Date createTime;//更新者@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateBy;//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;//备注private String remark;private String delFlag;//由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解// 让mybatis在查表时不查询这个字段@TableField(exist = false)private List<Menu> children;
}
第三步:service层新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;import java.util.List;/*** 菜单权限表(Menu)表服务接口** @author makejava* @since 2023-10-18 20:55:48*/
public interface MenuService extends IService<Menu> {//查询用户权限信息List<String> selectPermsByUserId(Long userId);//查询用户的路由信息,也就是权限菜单List<Menu> selectRouterMenuTreeByUserId(Long userId);ResponseResult selectAllMenu(Menu menu);ResponseResult editMenu(Menu menu);}
第四步:impl层新增
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 菜单权限表(Menu)表服务实现类** @author makejava* @since 2023-10-18 20:55:48*/
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {@Autowiredprivate MenuService menuService;//根据用户id查询权限关键字@Overridepublic List<String> selectPermsByUserId(Long userId) {//如果用户id为1代表管理员,roles 中只需要有admin,// permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限if(SecurityUtils.isAdmin()) {LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);//由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapperList<Menu> menuList = list(lambdaQueryWrapper);//我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式List<String> permissions = menuList.stream().map(new Function<Menu, String>() {@Overridepublic String apply(Menu menu) {String perms = menu.getPerms();return perms;}}).collect(Collectors.toList());return permissions;}//否则返回这个用户所具有的权限//这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联MenuMapper menuMapper = getBaseMapper();//我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回return menuMapper.selectPermsByUserId(userId);}@Overridepublic List<Menu> selectRouterMenuTreeByUserId(Long userId) {MenuMapper menuMapper = getBaseMapper();List<Menu> menus = null;//如果是管理员,返回所有if(SecurityUtils.isAdmin()){menus = menuMapper.selectAllRoutersMenu();}else {//如果不是管理员,返回对应用户的菜单menus = menuMapper.selectRoutersMenuTreeByUserId(userId);}//因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建TreeList<Menu> menuTree = buildMenuTree(menus,0L);return menuTree;}@Overridepublic ResponseResult selectAllMenu(Menu menu) {//可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());//排序 parent_id和order_numlambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);List<Menu> menus = list(lambdaQueryWrapper);List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);return ResponseResult.okResult(adminMenuVos);}@Overridepublic ResponseResult editMenu(Menu menu) {if(menu.getParentId().equals(menu.getId())){return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");}updateById(menu);return ResponseResult.okResult();}/*** 构建MenuTree* 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children* @param menus* @return*/private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {//转化流处理List<Menu> menuTree = menus.stream()//过滤掉除一级菜单之外的菜单.filter(menu -> menu.getParentId().equals(parentId))//然后将获取其子菜单设置到children字段,并返回.map(m -> m.setChildren(gerChildren(m, menus))).collect(Collectors.toList());return menuTree;}//获取当前菜单的子菜单private List<Menu> gerChildren(Menu menu, List<Menu> menus) {//流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤List<Menu> children = menus.stream().filter(m -> m.getParentId().equals(menu.getId()))//这里其实不必要写,这一步的逻辑是如果有三级,//可以把流对象中再过筛选出子菜单设置给对应的children并返回.map(m -> m.setChildren(gerChildren(m,menus))).collect(Collectors.toList());return children;}}
3.2.3 测试
4. 删除菜单
能够删除菜单,但是如果要删除的菜单有子菜单则提示:存在子菜单不允许删除 并且删除失败
4.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
DELETE | content/article/{menuId} | 是 |
请求参数PathVariable参数:
menuId:要删除菜单的id
响应格式:
{"code":200,"msg":"操作成功"
}
如果有子菜单
{"code":500,"msg":"存在子菜单不允许删除"
}
4.2 代码实现
第一步:controller层新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/menu")
public class MenuController {@Autowiredprivate MenuService menuService;//查询菜单列表@GetMapping("/list")public ResponseResult selectAllMenu(Menu menu){return menuService.selectAllMenu(menu);}//新增菜单@PostMappingpublic ResponseResult add(@RequestBody Menu menu) {menuService.save(menu);return ResponseResult.okResult();}@GetMapping("/{menuId}")public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){Menu menu = menuService.getById(menuId);return ResponseResult.okResult(menu);}@PutMappingpublic ResponseResult editMenu(@RequestBody Menu menu){return menuService.editMenu(menu);}@DeleteMapping("/{menuId}")public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){return menuService.deleteMenu(menuId);}}
第二步:service层新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;import java.util.List;/*** 菜单权限表(Menu)表服务接口** @author makejava* @since 2023-10-18 20:55:48*/
public interface MenuService extends IService<Menu> {//查询用户权限信息List<String> selectPermsByUserId(Long userId);//查询用户的路由信息,也就是权限菜单List<Menu> selectRouterMenuTreeByUserId(Long userId);ResponseResult selectAllMenu(Menu menu);ResponseResult editMenu(Menu menu);ResponseResult deleteMenu(Long menuId);
}
第三步:impl层新增
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 菜单权限表(Menu)表服务实现类** @author makejava* @since 2023-10-18 20:55:48*/
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {@Autowiredprivate MenuService menuService;//根据用户id查询权限关键字@Overridepublic List<String> selectPermsByUserId(Long userId) {//如果用户id为1代表管理员,roles 中只需要有admin,// permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限if(SecurityUtils.isAdmin()) {LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);//由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapperList<Menu> menuList = list(lambdaQueryWrapper);//我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式List<String> permissions = menuList.stream().map(new Function<Menu, String>() {@Overridepublic String apply(Menu menu) {String perms = menu.getPerms();return perms;}}).collect(Collectors.toList());return permissions;}//否则返回这个用户所具有的权限//这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联MenuMapper menuMapper = getBaseMapper();//我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回return menuMapper.selectPermsByUserId(userId);}@Overridepublic List<Menu> selectRouterMenuTreeByUserId(Long userId) {MenuMapper menuMapper = getBaseMapper();List<Menu> menus = null;//如果是管理员,返回所有if(SecurityUtils.isAdmin()){menus = menuMapper.selectAllRoutersMenu();}else {//如果不是管理员,返回对应用户的菜单menus = menuMapper.selectRoutersMenuTreeByUserId(userId);}//因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建TreeList<Menu> menuTree = buildMenuTree(menus,0L);return menuTree;}@Overridepublic ResponseResult selectAllMenu(Menu menu) {//可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());//排序 parent_id和order_numlambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);List<Menu> menus = list(lambdaQueryWrapper);List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);return ResponseResult.okResult(adminMenuVos);}@Overridepublic ResponseResult editMenu(Menu menu) {if(menu.getParentId().equals(menu.getId())){return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");}updateById(menu);return ResponseResult.okResult();}@Overridepublic ResponseResult deleteMenu(Long menuId) {Menu menu = menuService.getById(menuId);//如果该菜单有子菜单,那么就提示不能删除,逻辑就是查询菜单表中是否有父菜单id是当前菜单idLambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(Menu::getParentId,menuId);List<Menu> menus = list(lambdaQueryWrapper);if(!menus.isEmpty()) {return ResponseResult.okResult(500, "存在子菜单不允许删除");}removeById(menuId);return ResponseResult.okResult();}/*** 构建MenuTree* 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children* @param menus* @return*/private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {//转化流处理List<Menu> menuTree = menus.stream()//过滤掉除一级菜单之外的菜单.filter(menu -> menu.getParentId().equals(parentId))//然后将获取其子菜单设置到children字段,并返回.map(m -> m.setChildren(gerChildren(m, menus))).collect(Collectors.toList());return menuTree;}//获取当前菜单的子菜单private List<Menu> gerChildren(Menu menu, List<Menu> menus) {//流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤List<Menu> children = menus.stream().filter(m -> m.getParentId().equals(menu.getId()))//这里其实不必要写,这一步的逻辑是如果有三级,//可以把流对象中再过筛选出子菜单设置给对应的children并返回.map(m -> m.setChildren(gerChildren(m,menus))).collect(Collectors.toList());return children;}
}
4.3 测试
十二、后台模块-角色列表
1. 查询角色
需要有角色列表分页查询的功能。
要求能够针对角色名称进行模糊查询。
要求能够针对状态进行查询。
要求按照role_sort进行升序排列。
1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | system/role/list | 是 |
Query格式请求参数:
pageNum: 页码pageSize: 每页条数roleName:角色名称status:状态
响应格式:
{"code":200,"data":{"rows":[{"id":"12","roleKey":"link","roleName":"友链审核员","roleSort":"1","status":"0"}],"total":"1"},"msg":"操作成功"
}
1.2 代码实现
第一步:新建RoleController新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}
}
第二步:service层新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;import java.util.List;/*** 角色信息表(Role)表服务接口** @author makejava* @since 2023-10-18 21:04:06*/
public interface RoleService extends IService<Role> {List<String> selectRoleKeyByUserId(Long userId);ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
}
第三步:impl层新增
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;/*** 角色信息表(Role)表服务实现类** @author makejava* @since 2023-10-18 21:04:06*/
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {//根据用户id查询角色信息@Overridepublic List<String> selectRoleKeyByUserId(Long userId) {//如果userId为1,那么角色权限字符串就只需要返回一个adminif(userId==1L){List<String> roles = new ArrayList<>();roles.add("admin");return roles;}//如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询//对应的角色权限字符串//这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作RoleMapper roleMapper = getBaseMapper();return roleMapper.selectRoleKeyByUserId(userId);}@Overridepublic ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());lambdaQueryWrapper.orderByAsc(Role::getRoleSort);Page<Role> page = new Page<>(pageNum,pageSize);page(page,lambdaQueryWrapper);PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());return ResponseResult.okResult(pageVo);}
}
1.3 测试
2. 改变角色状态
要求能够修改角色的停启用状态
2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | system/role/changeStatus | 是 |
请求体:
{"roleId":"11","status":"1"
}
响应格式:
{"code":200,"msg":"操作成功"
}
2.2 代码实现
第一步:controller层新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}@PutMapping("/changeStatus")public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){return roleService.changeRoleStatus(changeRoleStatusDto);}
}
第二步:domain/dto新增
package com.keke.domain.dto;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChangeRoleStatusDto {private Long roleId;//角色状态(0正常 1停用)private String status;}
第三步:service层新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;import java.util.List;/*** 角色信息表(Role)表服务接口** @author makejava* @since 2023-10-18 21:04:06*/
public interface RoleService extends IService<Role> {List<String> selectRoleKeyByUserId(Long userId);ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
}
第四步:impl层新增
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;/*** 角色信息表(Role)表服务实现类** @author makejava* @since 2023-10-18 21:04:06*/
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {//根据用户id查询角色信息@Overridepublic List<String> selectRoleKeyByUserId(Long userId) {//如果userId为1,那么角色权限字符串就只需要返回一个adminif(userId==1L){List<String> roles = new ArrayList<>();roles.add("admin");return roles;}//如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询//对应的角色权限字符串//这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作RoleMapper roleMapper = getBaseMapper();return roleMapper.selectRoleKeyByUserId(userId);}@Overridepublic ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());lambdaQueryWrapper.orderByAsc(Role::getRoleSort);Page<Role> page = new Page<>(pageNum,pageSize);page(page,lambdaQueryWrapper);PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());return ResponseResult.okResult(pageVo);}@Overridepublic ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {Long roleId = changeRoleStatusDto.getRoleId();Role role = getById(roleId);role.setStatus(changeRoleStatusDto.getStatus());updateById(role);return ResponseResult.okResult();}
}
2.3 测试
把该用户启用
数据库中状态正常
3. 新增角色
需要提供新增角色的功能。新增角色时能够直接设置角色所关联的菜单权限
首先应该获取权限菜单数,勾选,然后才能新增
分析下来,有两个接口需要实现
3.1 获取菜单权限树接口
3.1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | /system/menu/treeselect | 是 |
无需请求参数
响应格式:
{"code":200,"data":[{"children":[],"id":"2023","label":"写博文","parentId":"0"},{"children":[{"children":[{"children":[],"id":"1001","label":"用户查询","parentId":"100"},{"children":[],"id":"1007","label":"重置密码","parentId":"100"}],"id":"100","label":"用户管理","parentId":"1"},{"children":[{"children":[],"id":"2024","label":"友链新增","parentId":"2022"},{"children":[],"id":"2025","label":"友链修改","parentId":"2022"},{"children":[],"id":"2026","label":"友链删除","parentId":"2022"},{"children":[],"id":"2027","label":"友链查询","parentId":"2022"}],"id":"2022","label":"友链管理","parentId":"2017"},{"children":[],"id":"2021","label":"标签管理","parentId":"2017"}],"id":"2017","label":"内容管理","parentId":"0"}],"msg":"操作成功"
}
3.1.2 代码实现
3.1.3 测试
3.2 新增角色接口
3.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
POST | system/role | 是 |
请求体:
{"roleName":"测试新增角色","roleKey":"wds","roleSort":0,"status":"0","menuIds":["1","100"],"remark":"我是角色备注"
}
响应格式:
{"code":200,"msg":"操作成功"
}
3.2.2 代码实现
第一步:domain/dto层新增
package com.keke.domain.dto;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;
import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminAddRoleDto {//角色名称private String roleName;//角色权限字符串private String roleKey;//显示顺序private Integer roleSort;//角色状态(0正常 1停用)private String status;//备注private String remark;//关联的menuIdList<Long> menuIds;
}
第二步:domain/entity层新增
package com.keke.domain.entity;import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;/*** 角色和菜单关联表(RoleMenu)表实体类** @author makejava* @since 2023-10-23 17:00:27*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_role_menu")
public class RoleMenu {//角色IDprivate Long roleId;//菜单IDprivate Long menuId;}
第三步:mapper层新增RoleMenuMapper
package com.keke.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.keke.domain.entity.RoleMenu;/*** 角色和菜单关联表(RoleMenu)表数据库访问层** @author makejava* @since 2023-10-23 17:01:09*/
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {}
第四步:service新增RoleMenuService
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.entity.RoleMenu;/*** 角色和菜单关联表(RoleMenu)表服务接口** @author makejava* @since 2023-10-23 17:01:09*/
public interface RoleMenuService extends IService<RoleMenu> {}
第五步:impl层新增RoleMenuServiceImpl
package com.keke.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.entity.RoleMenu;
import com.keke.mapper.RoleMenuMapper;
import com.keke.service.RoleMenuService;
import org.springframework.stereotype.Service;/*** 角色和菜单关联表(RoleMenu)表服务实现类** @author makejava* @since 2023-10-23 17:01:09*/
@Service("roleMenuService")
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> implements RoleMenuService {}
第六步:controller的RoleController新增
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}@PutMapping("/changeStatus")public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){return roleService.changeRoleStatus(changeRoleStatusDto);}@PostMappingpublic ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){return roleService.addRole(adminAddRoleDto);}}
第七步:service层的RoleService新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;import java.util.List;/*** 角色信息表(Role)表服务接口** @author makejava* @since 2023-10-18 21:04:06*/
public interface RoleService extends IService<Role> {List<String> selectRoleKeyByUserId(Long userId);ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);}
第八步:impl层的RoleServiceImpl新增,记得方法加上@Transactional注解
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleMenuService;
import com.keke.service.RoleService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 角色信息表(Role)表服务实现类** @author makejava* @since 2023-10-18 21:04:06*/
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {@Autowiredprivate RoleMenuService roleMenuService;//根据用户id查询角色信息@Overridepublic List<String> selectRoleKeyByUserId(Long userId) {//如果userId为1,那么角色权限字符串就只需要返回一个adminif(userId==1L){List<String> roles = new ArrayList<>();roles.add("admin");return roles;}//如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询//对应的角色权限字符串//这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作RoleMapper roleMapper = getBaseMapper();return roleMapper.selectRoleKeyByUserId(userId);}@Overridepublic ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());lambdaQueryWrapper.orderByAsc(Role::getRoleSort);Page<Role> page = new Page<>(pageNum,pageSize);page(page,lambdaQueryWrapper);PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());return ResponseResult.okResult(pageVo);}@Overridepublic ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {Long roleId = changeRoleStatusDto.getRoleId();Role role = getById(roleId);role.setStatus(changeRoleStatusDto.getStatus());updateById(role);return ResponseResult.okResult();}@Transactional@Overridepublic ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {//Bean拷贝Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);//拿到菜单权限id集合List<Long> menuIds = adminAddRoleDto.getMenuIds();//流式处理转化,把每一个menuId都设置到该roleId下List<RoleMenu> roleMenuList = menuIds.stream().map(new Function<Long, RoleMenu>() {@Overridepublic RoleMenu apply(Long menuId) {RoleMenu roleMenu = new RoleMenu();roleMenu.setRoleId(role.getId());roleMenu.setMenuId(menuId);return roleMenu;}}).collect(Collectors.toList());//mp批量保存到数据库中roleMenuService.saveBatch(roleMenuList);//封装返回return ResponseResult.okResult();}
}
第九步:domain/entity的Role实体类修改四个字段为mp自动填充
package com.keke.domain.entity;import java.util.Date;
import java.io.Serializable;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;/*** 角色信息表(Role)表实体类** @author makejava* @since 2023-10-18 21:03:52*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_role")
public class Role {//角色IDprivate Long id;//角色名称private String roleName;//角色权限字符串private String roleKey;//显示顺序private Integer roleSort;//角色状态(0正常 1停用)private String status;//删除标志(0代表存在 1代表删除)private String delFlag;//创建者@TableField(fill = FieldFill.INSERT)private Long createBy;//创建时间@TableField(fill = FieldFill.INSERT)private Date createTime;//更新者@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateBy;//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;//备注private String remark;
}
3.2.3 测试
4. 修改角色
需要提供修改角色的功能。修改角色时可以修改角色所关联的菜单权限
这里可以分析到修改角色有三个接口,一个点击是修改角色后,角色信息的回显接口,回显中有一个菜单权限树接口,另一个是修改角色的接口
4.1 角色信息回显接口
4.1.1接口分析
请求路径 | 请求方式 | 是否需求token头 |
---|---|---|
system/role/{id} | Get | 是 |
请求参数PathVariable格式:
id: 角色id
响应格式:
{"code":200,"data":{"id":"11","remark":"嘎嘎嘎","roleKey":"aggag","roleName":"嘎嘎嘎","roleSort":"5","status":"0"},"msg":"操作成功"
}
4.1.2 代码实现
第一步:controller层
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}@PutMapping("/changeStatus")public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){return roleService.changeRoleStatus(changeRoleStatusDto);}@PostMappingpublic ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){return roleService.addRole(adminAddRoleDto);}@GetMapping("/{id}")public ResponseResult getRoleInfo(@PathVariable("id") Long id){return roleService.getRoleInfo(id);}
}
第二步:domain/vo层新增
package com.keke.domain.vo;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminRoleVo {//角色IDprivate Long id;//角色名称private String roleName;//角色权限字符串private String roleKey;//显示顺序private Integer roleSort;//角色状态(0正常 1停用)private String status;//备注private String remark;
}
第三步:service层
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;import java.util.List;/*** 角色信息表(Role)表服务接口** @author makejava* @since 2023-10-18 21:04:06*/
public interface RoleService extends IService<Role> {List<String> selectRoleKeyByUserId(Long userId);ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);ResponseResult getRoleInfo(Long id);
}
第四步:impl层新增
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.AdminRoleVo;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleMenuService;
import com.keke.service.RoleService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 角色信息表(Role)表服务实现类** @author makejava* @since 2023-10-18 21:04:06*/
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {@Autowiredprivate RoleMenuService roleMenuService;//根据用户id查询角色信息@Overridepublic List<String> selectRoleKeyByUserId(Long userId) {//如果userId为1,那么角色权限字符串就只需要返回一个adminif(userId==1L){List<String> roles = new ArrayList<>();roles.add("admin");return roles;}//如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询//对应的角色权限字符串//这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作RoleMapper roleMapper = getBaseMapper();return roleMapper.selectRoleKeyByUserId(userId);}@Overridepublic ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());lambdaQueryWrapper.orderByAsc(Role::getRoleSort);Page<Role> page = new Page<>(pageNum,pageSize);page(page,lambdaQueryWrapper);PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());return ResponseResult.okResult(pageVo);}@Overridepublic ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {Long roleId = changeRoleStatusDto.getRoleId();Role role = getById(roleId);role.setStatus(changeRoleStatusDto.getStatus());updateById(role);return ResponseResult.okResult();}@Transactional@Overridepublic ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {//Bean拷贝Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);//拿到菜单权限id集合List<Long> menuIds = adminAddRoleDto.getMenuIds();//流式处理转化,把每一个menuId都设置到该roleId下List<RoleMenu> roleMenuList = menuIds.stream().map(new Function<Long, RoleMenu>() {@Overridepublic RoleMenu apply(Long menuId) {RoleMenu roleMenu = new RoleMenu();roleMenu.setRoleId(role.getId());roleMenu.setMenuId(menuId);return roleMenu;}}).collect(Collectors.toList());//mp批量保存到数据库中roleMenuService.saveBatch(roleMenuList);//封装返回return ResponseResult.okResult();}@Overridepublic ResponseResult getRoleInfo(Long id) {Role role = getById(id);AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);return ResponseResult.okResult(adminRoleVo);}
}
4.1.3 测试
回显成功
4.2 菜单权限树接口
4.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
Get | /system/menu/roleMenuTreeselect/{id} | 是 |
请求参数PathVariable格式:
id: 角色id
响应格式:
字段介绍
menus:菜单树
checkedKeys:角色所关联的菜单权限id列表
{"code":200,"data":{"menus":[{"children":[],"id":"2023","label":"写博文","parentId":"0"},{"children":[{"children":[{"children":[],"id":"1001","label":"用户查询","parentId":"100"},{"children":[],"id":"1002","label":"用户新增","parentId":"100"},{"children":[],"id":"1003","label":"用户修改","parentId":"100"},{"children":[],"id":"1004","label":"用户删除","parentId":"100"},{"children":[],"id":"1005","label":"用户导出","parentId":"100"},{"children":[],"id":"1006","label":"用户导入","parentId":"100"},{"children":[],"id":"1007","label":"重置密码","parentId":"100"}],"id":"100","label":"用户管理","parentId":"1"},{"children":[{"children":[],"id":"1008","label":"角色查询","parentId":"101"},{"children":[],"id":"1009","label":"角色新增","parentId":"101"},{"children":[],"id":"1010","label":"角色修改","parentId":"101"},{"children":[],"id":"1011","label":"角色删除","parentId":"101"},{"children":[],"id":"1012","label":"角色导出","parentId":"101"}],"id":"101","label":"角色管理","parentId":"1"},{"children":[{"children":[],"id":"1013","label":"菜单查询","parentId":"102"},{"children":[],"id":"1014","label":"菜单新增","parentId":"102"},{"children":[],"id":"1015","label":"菜单修改","parentId":"102"},{"children":[],"id":"1016","label":"菜单删除","parentId":"102"}],"id":"102","label":"菜单管理","parentId":"1"}],"id":"1","label":"系统管理","parentId":"0"},{"children":[{"children":[],"id":"2019","label":"文章管理","parentId":"2017"},{"children":[{"children":[],"id":"2028","label":"导出分类","parentId":"2018"}],"id":"2018","label":"分类管理","parentId":"2017"},{"children":[{"children":[],"id":"2024","label":"友链新增","parentId":"2022"},{"children":[],"id":"2025","label":"友链修改","parentId":"2022"},{"children":[],"id":"2026","label":"友链删除","parentId":"2022"},{"children":[],"id":"2027","label":"友链查询","parentId":"2022"}],"id":"2022","label":"友链管理","parentId":"2017"},{"children":[],"id":"2021","label":"标签管理","parentId":"2017"}],"id":"2017","label":"内容管理","parentId":"0"}],"checkedKeys":["1001" ]},"msg":"操作成功"
}
4.2.2 代码实现(待实现)
4.2.3 测试(待实现)
4.3 修改角色接口
4.3.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | system/role | 是 |
请求体:
{"id":"13","remark":"我是角色备注","roleKey":"wds","roleName":"测试新增角色","roleSort":0,"status":"0","menuIds":["1","100","1001"]
}
响应体
{"code":200,"msg":"操作成功"
}
4.3.2 代码实现
第一步:domain/dto层新增
package com.keke.domain.dto;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;
import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
public class EditRoleDto {//角色IDprivate Long id;//角色名称private String roleName;//角色权限字符串private String roleKey;//显示顺序private Integer roleSort;//角色状态(0正常 1停用)private String status;//备注private String remark;//权限idprivate List<Long> menuIds;
}
第二步:controller层新增EditRole接口
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}@PutMapping("/changeStatus")public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){return roleService.changeRoleStatus(changeRoleStatusDto);}@PostMappingpublic ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){return roleService.addRole(adminAddRoleDto);}@GetMapping("/{id}")public ResponseResult getRoleInfo(@PathVariable("id") Long id){return roleService.getRoleInfo(id);}@PutMappingpublic ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){return roleService.editRole(editRoleDto);}
}
第二步:service新增
package com.keke.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;import java.util.List;/*** 角色信息表(Role)表服务接口** @author makejava* @since 2023-10-18 21:04:06*/
public interface RoleService extends IService<Role> {List<String> selectRoleKeyByUserId(Long userId);ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);ResponseResult getRoleInfo(Long id);ResponseResult editRole(EditRoleDto editRoleDto);
}
第三步:impl新增,逻辑和添加用户一样
package com.keke.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.AdminRoleVo;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleMenuService;
import com.keke.service.RoleService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*** 角色信息表(Role)表服务实现类** @author makejava* @since 2023-10-18 21:04:06*/
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {@Autowiredprivate RoleMenuService roleMenuService;//根据用户id查询角色信息@Overridepublic List<String> selectRoleKeyByUserId(Long userId) {//如果userId为1,那么角色权限字符串就只需要返回一个adminif(userId==1L){List<String> roles = new ArrayList<>();roles.add("admin");return roles;}//如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询//对应的角色权限字符串//这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作RoleMapper roleMapper = getBaseMapper();return roleMapper.selectRoleKeyByUserId(userId);}@Overridepublic ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());lambdaQueryWrapper.orderByAsc(Role::getRoleSort);Page<Role> page = new Page<>(pageNum,pageSize);page(page,lambdaQueryWrapper);PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());return ResponseResult.okResult(pageVo);}@Overridepublic ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {Long roleId = changeRoleStatusDto.getRoleId();Role role = getById(roleId);role.setStatus(changeRoleStatusDto.getStatus());updateById(role);return ResponseResult.okResult();}@Transactional@Overridepublic ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {//Bean拷贝Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);//拿到菜单权限id集合List<Long> menuIds = adminAddRoleDto.getMenuIds();//流式处理转化,把每一个menuId都设置到该roleId下List<RoleMenu> roleMenuList = menuIds.stream().map(new Function<Long, RoleMenu>() {@Overridepublic RoleMenu apply(Long menuId) {RoleMenu roleMenu = new RoleMenu();roleMenu.setRoleId(role.getId());roleMenu.setMenuId(menuId);return roleMenu;}}).collect(Collectors.toList());//mp批量保存到数据库中roleMenuService.saveBatch(roleMenuList);//封装返回return ResponseResult.okResult();}@Overridepublic ResponseResult getRoleInfo(Long id) {Role role = getById(id);AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);return ResponseResult.okResult(adminRoleVo);}@Overridepublic ResponseResult editRole(EditRoleDto editRoleDto) {Role role = BeanCopyUtils.copyBean(editRoleDto, Role.class);List<Long> menuIds = editRoleDto.getMenuIds();List<RoleMenu> roleMenuList = menuIds.stream().map(new Function<Long, RoleMenu>() {@Overridepublic RoleMenu apply(Long menuId) {RoleMenu roleMenu = new RoleMenu();roleMenu.setRoleId(role.getId());roleMenu.setMenuId(menuId);return roleMenu;}}).collect(Collectors.toList());roleMenuService.saveBatch(roleMenuList);return ResponseResult.okResult();}
}
4.3.3 测试(待实现)
5. 删除角色
删除固定的某个角色(逻辑删除)
5.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
DELETE | system/role/{id} | 是 |
请求参数PathVariable格式:
id:要删除的角色id
响应格式:
{"code":200,"msg":"操作成功"
}
5.2 代码实现
controller层新增接口,逻辑较少直接写
package com.keke.controller;import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system/role")
public class RoleController {@Autowiredprivate RoleService roleService;@GetMapping("/list")public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){return roleService.selectPageRole(role,pageNum,pageSize);}@PutMapping("/changeStatus")public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){return roleService.changeRoleStatus(changeRoleStatusDto);}@PostMappingpublic ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){return roleService.addRole(adminAddRoleDto);}@GetMapping("/{id}")public ResponseResult getRoleInfo(@PathVariable("id") Long id){return roleService.getRoleInfo(id);}@PutMappingpublic ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){return roleService.editRole(editRoleDto);}@DeleteMapping("/{id}")public ResponseResult deleteRoleById(@PathVariable("id") Long id){roleService.removeById(id);return ResponseResult.okResult();}
}
5.3 测试
只是逻辑删除
相关文章:

博客后台模块续更(五)
十一、后台模块-菜单列表 菜单指的是权限菜单,也就是一堆权限字符串 1. 查询菜单 1.1 接口分析 需要展示菜单列表,不需要分页。可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询。菜单要按照父菜单id和orderNum进行排序 请求方式 请求路径…...

手写一个PrattParser基本运算解析器4: 简述iOS的编译过程
点击查看 基于Swift的PrattParser项目 iOS项目的编译过程与PrattParser解析器 前面三篇我们看到了PrattParser解析器的工作原理, 工作过程, 我们了解到PrattParser解析器实际上是模拟了编译过程中的 词法分析 、语法分析 、语义分析 、 中间代码生成 这几个编译前端过程. 那么P…...

【Java集合类面试六】、 HashMap有什么特点?
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:HashMap有什么特点&…...

基于LSTM的天气预测 - 时间序列预测 计算机竞赛
0 前言 🔥 优质竞赛项目系列,今天要分享的是 机器学习大数据分析项目 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/po…...

SpringBoot AOP + Redis 延时双删功能实战
一、业务场景 在多线程并发情况下,假设有两个数据库修改请求,为保证数据库与redis的数据一致性,修改请求的实现中需要修改数据库后,级联修改Redis中的数据。 请求一:A修改数据库数据 B修改Redis数据 请求二ÿ…...

【Java集合类面试七】、 JDK7和JDK8中的HashMap有什么区别?
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:JDK7和JDK8中的HashMap有…...
el-tree 获取过滤后的树结构
正常来说element框架应该返回的,但实际上没有,只能自己处理了 递归处理,思路就是赋值,如果是自己过滤到的数据就push进去,不是就不要 let newCheckTree [] let tree get_tree(treeData,newCheckTree); //获取过滤…...
Windows连接SFTP服务
最近有个新需求需要通过SFTP方式连接到一个FTP中下载相关内容 1.使用命令行方式 在cmd中使用如下命令 sftp -P [port] [username]ip #示例 sftp -P 666 ftp123.123.123.123然后弹出的提示输入yes,再输入密码就可以了。 2.使用资源管理器方式 普通FTP可以使用资源…...

《红蓝攻防对抗实战》五.内网探测协议出网之DNS协议探测出网
DNS(Domain Name System)即域名解析系统,可将域名解析到对应访问IP。下面我们还是以系统自带命令为案例,进行演示DNS协议探测出网。 目录 一.Windows系统探测DNS协议出网 二.Linux系统探测DNS协议出网 1. Dig命令 2.Nslookup命…...

计算机算法分析与设计(18)---回溯法(介绍、子集和问题C++代码)
文章目录 一、回溯法介绍二、子集和问题2.1 知识概述2.2 代码编写 一、回溯法介绍 1. 回溯法(back tracking)是一种选优搜索法,又称为试探法,有“通用的解题法”之称,按选优条件向前搜索,以达到目标。但当探…...

[Hive] explode
在 Hive 中,explode 函数用于将数组(Array)或者Map类型的列拆分成多行, 每个元素或键值对为一行。这允许我们在查询中对数组或 Map 进行扁平化操作。 下面是使用 explode 函数的示例: 假设我们有一个包含数组字段的表…...
2023年10月22日找工作面试交流遇到的基本问题
交叉编译解决的痛点问题 不同硬件体系结构之间的编译问题。嵌入式系统开发需要在主机上编写代码。提高效率和节省时间。软件移植和管理依赖关系。 不同硬件体系结构之间的编译问题:例如,你开发了一个针对Intel x86架构的应用程序,但想要在Ra…...

如何判断要不要用振动技术来进行设备预测性维护
在现代工业设备运行过程中,及时发现设备故障并进行维修对于确保生产线的正常运行至关重要。振动分析技术作为一种先进的设备监测和预测性维护方法,通过实时监测和分析设备的振动信号,可以提前发现潜在故障,降低停机时间和维护成本…...

数据结构和算法——用C语言实现所有树形结构及相关算法
文章目录 前言树和森林基础概念二叉树二叉树的遍历二叉树的构造树和森林与二叉树之间的转化树和森林的遍历 满二叉树完全二叉树线索二叉树线索二叉树的构造寻找前驱和后继线索二叉树的遍历 最优二叉树(哈夫曼树)哈夫曼树的构造哈夫曼编码 二叉排序树&…...

OTA: Optimal Transport Assignment for Object Detection 论文和代码学习
OTA 原因步骤什么是最优传输策略标签分配的OT正标签分配负标签分配损失计算中心点距离保持稳定动态k的选取 整体流程代码使用 论文连接: 原因 1、全部按照一个策略如IOU来分配GT和Anchors不能得到全局最优,可能只能得到局部最优。 2、目前提出的ATSS和P…...

前后端交互—跨域与HTTP
跨域 代码下载 同源策略 同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。 MDN 官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这 是一个用于隔离潜在恶意文件的重要安全机制。 通俗的理解:浏览器规定&a…...
Error和Exception的关系以及区别
在Java中,Error 和 Exception 是两种不同类型的异常类,它们都继承自 java.lang.Throwable,但在用途和处理方式上有重要区别。 Error: Error 表示在程序运行过程中,通常由于系统或环境的严重问题而引起的异常情况。这些问题通常是无…...
Hive SQL 函数高阶应用场景
HIVE作为数据仓库处理常用工具,如同RDBMS关系型数据库中标准SQL语法一样,Hive SQL也内置了不少系统函数,满足于用户在不同场景下的数据分析需求,以提高开发SQL数据分析的效率。 我们可以使用show functions查看当下版本支持的函数…...
linux下C++开发环境搭建
一.安装GCC,GDB 1.1 先更新软件包安装源 sudo apt update1.2 安装编译器和调试器 sudo apt install build-essential gdb"build-essential" 是编译代码所需要的工具。 "gdb" 是调试器。1. build-essential:- "build-essential" 是一个用于Ubu…...
报错问题解决办法:Decryption error sun.security.rsa.RSAPadding.unpadV15
报错问题解决办法:Decryption error sun.security.rsa.RSAPadding.unpadV15 出现的问题 javax.crypto.BadPaddingException: Decryption errorat sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380) ~[na:1.8.0_131]at sun.security.rsa.RSAPadding.unpa…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...