csmall-passport(Day15)

news/2025/2/1 22:57:13/

1. 显示角色列表

当【添加管理员】时,必须在操作界面上提供一个可以选择【角色】的控件,且添加时必须确定某些角色,从而,新添加的管理员才会因为选择的角色而关联到某些权限,管理员才会具有某些操作权限!

关于选择【角色】,需要将当前系统中的【角色列表】显示在操作界面上!

关于Mapper层(数据访问层 / DAO(Data Access Object)层 / 持久层)

需要执行的SQL语句大致是:

SELECT id, name, description, sort FROM ams_role ORDER BY sort DESC, id

在根包下创建pojo.vo.RoleListItemVO类:

package cn.tedu.csmall.passport.pojo.vo;import lombok.Data;import java.io.Serializable;@Data
public class RoleListItemVO implements Serializable {private Long id;private String name;private String description;private Integer sort;}

在根包下创建mapper.RoleMapper接口,并添加抽象方法:

package cn.tedu.csmall.passport.mapper;import cn.tedu.csmall.passport.pojo.vo.RoleListItemVO;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public interface RoleMapper {List<RoleListItemVO> list();}

src/main/resources下的mapper文件夹中粘贴得到RoleMapper.xml,并在此文件中配置以上抽象方法映射的SQL语句:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.tedu.csmall.passport.mapper.RoleMapper"><select id="list" resultMap="ListResultMap">SELECT<include refid="ListQueryFields" />FROMams_roleORDER BYsort DESC, id</select><sql id="ListQueryFields"><if test="true">id, name, description, sort</if></sql><resultMap id="ListResultMap" type="cn.tedu.csmall.passport.pojo.vo.RoleListItemVO"><id column="id" property="id" /><result column="name" property="name" /><result column="description" property="description" /><result column="sort" property="sort" /></resultMap></mapper>

src/test/java下的根包下创建mapper.RoleMapperTests测试类,对以上方法进行测试:

package cn.tedu.csmall.passport.mapper;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@Slf4j
@SpringBootTest
public class RoleMapperTests {@AutowiredRoleMapper mapper;@Testvoid testList() {List<?> list = mapper.list();log.debug("查询列表完成,结果集的数量={}", list.size());for (Object item : list) {log.debug("{}", item);}}}

关于Service层(业务逻辑层 / 业务层)

在根包下创建service.IRoleService接口,并在接口中添加抽象方法:

package cn.tedu.csmall.passport.service;import cn.tedu.csmall.passport.pojo.vo.RoleListItemVO;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Transactional
public interface IRoleService {List<RoleListItemVO> list();}

在根包下创建service.impl.RoleServiceImpl类,实现以上接口,并实现抽象方法:

package cn.tedu.csmall.passport.service.impl;import cn.tedu.csmall.passport.mapper.RoleMapper;
import cn.tedu.csmall.passport.pojo.vo.RoleListItemVO;
import cn.tedu.csmall.passport.service.IRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Slf4j
@Service
public class RoleServiceImpl implements IRoleService {@Autowiredprivate RoleMapper roleMapper;public RoleServiceImpl() {log.debug("创建业务对象:RoleServiceImpl");}@Overridepublic List<RoleListItemVO> list() {log.debug("开始处理【查询角色列表】的业务");return roleMapper.list();}}

src/test/java下的根包下创建service.RoleServiceTests测试类,对以上方法进行测试:

package cn.tedu.csmall.passport.service;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@Slf4j
@SpringBootTest
public class RoleServiceTests {@AutowiredIRoleService service;@Testvoid testList() {List<?> list = service.list();log.debug("查询列表完成,结果集的数量={}", list.size());for (Object item : list) {log.debug("{}", item);}}}

关于Controller层(控制器层)

在根包下创建RoleController,并处理请求:

package cn.tedu.csmall.passport.controller;import cn.tedu.csmall.passport.pojo.vo.RoleListItemVO;
import cn.tedu.csmall.passport.service.IRoleService;
import cn.tedu.csmall.passport.web.JsonResult;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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;import java.util.List;@Slf4j
@RestController
@RequestMapping("/roles")
@Api(tags = "02. 角色管理模块")
public class RoleController {@Autowiredprivate IRoleService roleService;public RoleController() {log.info("创建控制器:RoleController");}@GetMapping("")@ApiOperation("查询角色列表")@ApiOperationSupport(order = 410)public JsonResult<List<RoleListItemVO>> list() {log.debug("开始处理【查询角色列表】的请求");List<RoleListItemVO> list = roleService.list();return JsonResult.ok(list);}}

完成后,启动项目,可以通过Knife4j的调试功能进行测试,注意:需要携带有效的JWT!

另外,如果响应结果时,不希望包含为null的属性,可以在application.properties中补充配置:

# 响应结果中,不显示为null的属性
spring.jackson.default-property-inclusion=non_null

2. 关于客户端提交数组格式的数据

在使用qs将对象转换成FormData格式的字符串时,如果原对象中有数组,默认情况下,数组数据会被转换成如下格式:

roleIds%5B0%5D=1&roleIds%5B1%5D=2

以上代码中,%5B表示[%5D表示]

可以在使用qs时进行配置,以设置数组数据的格式,例如:

let formData = this.qs.stringify(this.ruleForm, {'arrayFormat': 'repeat'});

arrayFormat属性的值为repeat时,FormData格式为:

roleIds=1&roleIds=2&roleIds=3

arrayFormat属性的值为indices时,FormData格式为:

roleIds%5B0%5D=1&roleIds%5B1%5D=2

arrayFormat属性的值为brackets时,FormData格式为:

roleIds%5B%5D=1&roleIds%5B%5D=2

其实,无论使用以上哪种方式,Spring MVC框架都可以正常接收到数组值!

3. 调整:添加管理员时关联角色

关于Mapper层

在服务器端,添加管理员时,需要添加管理员与角色的关联,否则,管理员没有关联角色,也就没有权限了!

需要执行的SQL语句大致是(每个管理员可以关联多个角色):

INSERT INTO ams_admin_role (admin_id, role_id) VALUES (?,?), (?,?), ... (?,?);

在根包下创建pojo.entity.AdminRole实体类:

package cn.tedu.csmall.passport.pojo.entity;import lombok.Data;import java.io.Serializable;
import java.time.LocalDateTime;@Data
public class AdminRole implements Serializable {private Long id;private Long adminId;private Long roleId;private LocalDateTime gmtCreate;private LocalDateTime gmtModified;}

在根包下创建mapper.AdminRoleMapper接口,并添加抽象方法:

package cn.tedu.csmall.passport.mapper;import cn.tedu.csmall.passport.pojo.entity.AdminRole;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public interface AdminRoleMapper {int insertBatch(List<AdminRole> adminRoleList);}

src/main/resources下的mapper文件夹中粘贴得到AdminRoleMapper.xml文件,并配置以上抽象方法映射的SQL语句:

<!-- int insertBatch(List<AdminRole> adminRoleList); -->
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">INSERT INTO ams_admin_role (admin_id, role_id, gmt_create, gmt_modified) VALUES <foreach collection="list" item="adminRole" separator=",">(#{adminRole.adminId}, #{adminRole.roleId}, #{adminRole.gmtCreate}, #{adminRole.gmtModified})</foreach>
</insert>

完成后,在src/test/java下的根包下创建mapper.AdminRoleMapperTests测试类,对以上功能进行测试:

package cn.tedu.csmall.passport.mapper;import cn.tedu.csmall.passport.pojo.entity.AdminRole;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;@Slf4j
@SpringBootTest
public class AdminRoleMapperTests {@AutowiredAdminRoleMapper mapper;@Testvoid testInsertBatch() {LocalDateTime now = LocalDateTime.now();List<AdminRole> adminRoleList = new ArrayList<>();for (int i = 1; i <= 3; i++) {AdminRole adminRole = new AdminRole();adminRole.setAdminId(5L);adminRole.setRoleId(i + 0L);adminRole.setGmtCreate(now);adminRole.setGmtModified(now);adminRoleList.add(adminRole);}int rows = mapper.insertBatch(adminRoleList);log.debug("批量插入管理员与角色的关联关系数据成功,受影响的行数={}", rows);}}

关于Service层

由于此前已经实现了“增加管理员”的业务,只是尚未实现“插入管理员与角色的关联关系”,所以,只需要在原有代码基础上调整即可,并不需要创建新的接口、实现类!

原有代码中,关于“增加管理员”的业务方法的声明是:

void addNew(AdminAddNewDTO adminAddNewDTO);

以上方法中,参数AdminAddNewDTO中并不包含若干个角色id,所以,需要先补充声明此属性:

@Data
public class AdminAddNewDTO implements Serializable {// 省略原有代码,以下是新增的属性/*** 管理员的角色*/private Long[] roleIds;}

然后,在AdminServiceImpl中,先自动装配AdminRoleMapper对象:

@Autowired
private AdminRoleMapper adminRoleMapper;

然后,在原有的addNew()方法的最后补充:

// 插入管理员与角色的关联关系
log.debug("准备插入管理员与角色的关联关系");
LocalDateTime now = LocalDateTime.now();
Long[] roleIds = adminAddNewDTO.getRoleIds();
List<AdminRole> adminRoleList = new ArrayList<>();
for (int i = 0; i < roleIds.length; i++) {AdminRole adminRole = new AdminRole();adminRole.setAdminId(admin.getId());adminRole.setRoleId(roleIds[i]);adminRole.setGmtCreate(now);adminRole.setGmtModified(now);adminRoleList.add(adminRole);
}
rows = adminRoleMapper.insertBatch(adminRoleList);
if (rows != roleIds.length) {// 是:抛出ServiceException(ERR_INSERT)String message = "添加管理员失败,服务器忙,请稍后再尝试![错误代码:2]";log.warn(message);throw new ServiceException(ServiceCode.ERR_INSERT, message);
}
log.debug("添加管理员完成!");

完成后,可以通过AdminServiceTests中原有的测试方法进行测试,也可以在重启项目后,通过前端页面来添加管理员,都会是成功的,此功能开发至此就已经完成!

4. 修复:增加管理员时,检查角色id是否存在

客户端提交的角色id是Long[] roleIds,可以通过WHERE id IN (?,?)作为条件的查询进行检查,需要执行的SQL语句大致是:

SELECT count(*) FROM ams_role WHERE id IN (?,?, ... ?);

RoleMapper.java接口中补充抽象方法:

int countByIds(Long[] ids);

RoleMapper.xml中配置以上抽象方法映射的SQL语句:

<!-- int countByIds(Long[] ids); -->
<select id="countByIds" resultType="int">SELECT count(*) FROM ams_role WHERE id IN (<foreach collection="array" item="id" separator=",">#{id}</foreach>)
</select>

RoleMapperTests中编写并执行测试:

@Test
void testCountByIds() {Long[] ids = {1L,2L,3L,7L,9L};int count = mapper.countByIds(ids);log.debug("根据id({})进行统计,统计结果={}", Arrays.toString(ids), count);
}

最后,在AdminServiceImpladdNew()方法,在插入管理员与角色关联之前,补充:

// 需要补充:自动装配RoleMapper对象// 检查各角色id是否存在
Long[] roleIds = adminAddNewDTO.getRoleIds(); // 从原下方代码中移动到此处
int countByIds = roleMapper.countByIds(roleIds);
if (countByIds != roleIds.length) {// 是:抛出ServiceException(ERR_BAD_REQUEST)String message = "添加管理员失败,角色数据错误!";log.warn(message);throw new ServiceException(ServiceCode.ERR_BAD_REQUEST, message);
}

4. 修复:删除管理员时,需要删除关联数据

当删除管理员时,对应的“管理员与角色的关联”数据也需要一并删除!

关于Mapper层

需要执行的SQL语句大致是:

DELETE FROM ams_admin_role WHERE admin_id=?

AdminRoleMapper.java接口中添加抽象方法:

int deleteByAdmin(Long adminId);

AdminMapper.xml中配置SQL:

<!-- int deleteByAdmin(Long adminId); -->
<delete id="deleteByAdmin">DELETE FROM ams_admin_role WHERE admin_id=#{adminId}
</delete>

AdminMapperTests中编写并执行测试:

@Test
void testDeleteByAdmin() {Long adminId = 17L;int rows = mapper.deleteByAdmin(adminId);log.debug("根据管理员【id={}】删除关联数据,受影响的行数={}", adminId, rows);
}

关于Service层

需要在现有的“删除管理员”的业务中,补充“从ams_admin_role表中删除此管理员的关联数据”。

AdminServiceImpl类中的deleteById()方法的最后补充:

// 删除“管理员与角色”的关联数据
rows = adminRoleMapper.deleteByAdmin(id);
// 判断返回值是否小于1
if (rows < 1) {// 抛出ServiceException,业务状态码:DELETE对应的常量String message = "删除管理员失败!服务器忙,请稍后再次尝试![错误代码:2]";log.warn(message);throw new ServiceException(ServiceCode.ERR_DELETE, message);
}

作业

完成:通过前端页面的操作,实现“启用”、“禁用”管理员账号。


http://www.ppmy.cn/news/618747.html

相关文章

Passport用户认证

1.介绍 1.在购物车之前功能&#xff0c;不需要对用户登陆进行判定 2.在购物车之后的功能必须对用户登录进行判定 3.在购物车功能中&#xff0c;必须对购物车进行判定&#xff0c;但是判定失败也 可以继续使用(程序可以继续执行cookie分支) 2.登陆的入口 1.直接点击登陆登录功能…

统一登录passport介绍

前一段时间&#xff0c;因为在开发中使用到公司的统一登录平台passport&#xff0c;所以就自己找了很多资料&#xff0c;详细的学习了一下它们的原理以及实现过程&#xff0c;这里就着重分享一下“跨域共享cookie和跨域共享session”。 场景&#xff1a;公司所有登陆都走的是统…

csmall-passport(Day14)

1. 使用JWT保存权限 在UserDetailsServiceImpl中&#xff0c;调用的adminMapper.getLoginInfoByUsername()中已经包含用户的权限&#xff0c;则&#xff0c;在返回的UserDetails对象中封装权限信息&#xff1a; UserDetails userDetails User.builder().username(loginAdmin…

全栈之初识 Passport Passport-jwt – Web安全的守护神

一、Passport 简介 passport.js是Nodejs中的一个做登录验证的中间件&#xff0c;极其灵活和模块化&#xff0c;并且可与Express、Sails等Web框架无缝集成。Passport功能单一&#xff0c;即只能做登录验证&#xff0c;但非常强大&#xff0c;支持本地账号验证和第三方账号登录验…

【详细】使用 passport.js 来完成登录验证

使用 passport.js 完成后台验证 转载自楼主个人博客 使用 passport.js 来完成登录验证 - 2016/6/22 先啰嗦一段背景 介绍一下项目所使用的技术栈。Node.js&#xff0c;使用 Express 来完成后端服务器的架构&#xff0c;这个时候就遇到了一个问题了。在我以前&#xff0c;是用 J…

Laravel 的 API 认证系统 Passport 三部曲(二、passport的具体使用)

GQ1994 关注 2018.04.20 09:31 字数 1152 阅读 1316评论 0喜欢 1 参考链接 Laravel 的 API 认证系统 Passport 三部曲(一、passport安装配置) Laravel 的 API 认证系统 Passport 引言 在使用前要先了解Auth2.0的使用方式和原理Laravel 的用户认证系统passport是专门做api令牌授…

Nodejs Passport 系列之四:Passport 源码剖析之 OAuth2 认证流程

前言 本文是笔者所总结的有关 Nodejs Passport 系列之一&#xff1b;本文将从源码分析的角度&#xff0c;来深入剖析 passport 的认证流程&#xff1b; 本文为作者原创作品&#xff0c;转载请注明出处&#xff1b; 综述 OAuth2orize 包模块扩展使得 Express 成为具备 OAuth…

建立无需build的react单页面SPA框架

vue、react这种前端渲染的框架&#xff0c;比较适合做SPA。如果用ejs做SPA&#xff08;Single Page Application&#xff09;&#xff0c;js代码控制好全局变量冲突不算严重&#xff0c;但dom元素用jquery操作会遇到很多的名称上的冲突&#xff08;tag、id、name&#xff09;。…