三、 后台管理系统后端开发
3.租赁管理
看房预约管理

根据条件分页查询预约信息

Alt+Enter,实现方法

创建方法、Generate statement
XML
<resultMap id="AppointmentVoMap" type="org.example.lease.web.admin.vo.appointment.AppointmentVo" autoMapping="true">
<id property="id" column="id"/>
<association property="apartmentInfo" javaType="org.example.lease.model.entity.ApartmentInfo"
autoMapping="true">
<id property="id" column="apartment_id"/>
<result property="name" column="apartment_name"/>
</association>
</resultMap>
<select id="pageAppointmentByQuery" resultMap="AppointmentVoMap">
select va.id,
va.user_id,
va.name,
va.phone,
va.appointment_time,
va.additional_info,
va.appointment_status,
ai.id apartment_id,
ai.name apartment_name,
ai.district_id,
ai.district_name,
ai.city_id,
ai.city_name,
ai.province_id,
ai.province_name
from view_appointment va
left join
apartment_info ai
on va.apartment_id = ai.id and ai.is_deleted = 0
<where>
va.is_deleted = 0
<if test="queryVo.provinceId != null">
and ai.province_id = #{queryVo.provinceId}
</if>
<if test="queryVo.cityId != null">
and ai.city_id = #{queryVo.cityId}
</if>
<if test="queryVo.districtId != null">
and ai.district_id = #{queryVo.districtId}
</if>
<if test="queryVo.apartmentId != null">
and va.apartment_id = #{queryVo.apartmentId}
</if>
<if test="queryVo.name != null and queryVo.name != ''">
and va.name like concat('%',#{queryVo.name},'%')
</if>
<if test="queryVo.phone != null and queryVo.phone != ''">
and va.phone like concat('%',#{queryVo.phone},'%')
</if>
</where>
</select>
成功

设置序列化后的时间字符串
格式
XML
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")


时区
XML
@JsonFormat(timezone = "GMT+8")
XML
spring:
jackson:
time-zone: GMT+8


根据ID更新预约状态

成功

租约管理

保存获更新租约信息


根据条件分页查询租约列表

Alt+Enter,实现方法

创建方法、Generate statement
XML
<resultMap id="agreementVoMap" type="org.example.lease.web.admin.vo.agreement.AgreementVo" autoMapping="true">
<id property="id" column="id"/>
<association property="apartmentInfo" javaType="org.example.lease.model.entity.ApartmentInfo"
autoMapping="true">
<id property="id" column="apartment_id"/>
<result property="name" column="apartment_name"/>
</association>
<association property="roomInfo" javaType="org.example.lease.model.entity.RoomInfo" autoMapping="true">
<id property="id" column="room_id"/>
</association>
<association property="paymentType" javaType="org.example.lease.model.entity.PaymentType" autoMapping="true">
<id property="id" column="payment_type_id"/>
<result property="name" column="payment_type_name"/>
</association>
<association property="leaseTerm" javaType="org.example.lease.model.entity.LeaseTerm" autoMapping="true">
<id property="id" column="lease_term_id"/>
</association>
</resultMap>
<select id="pageAgreementByQuery" resultMap="agreementVoMap">
select la.id,
la.phone,
la.name,
la.identification_number,
la.lease_start_date,
la.lease_end_date,
la.rent,
la.deposit,
la.status,
la.source_type,
la.additional_info,
ai.id apartment_id,
ai.name apartment_name,
ai.district_id,
ai.district_name,
ai.city_id,
ai.city_name,
ai.province_id,
ai.province_name,
ri.id room_id,
ri.room_number,
pt.id payment_type_id,
pt.name payment_type_name,
pt.pay_month_count,
lt.id lease_term_id,
lt.month_count,
lt.unit
from lease_agreement la
left join
apartment_info ai
on la.apartment_id = ai.id and ai.is_deleted = 0
left join
room_info ri
on la.room_id = ri.id and ri.is_deleted = 0
left join
payment_type pt
on la.payment_type_id = pt.id and pt.is_deleted = 0
left join
lease_term lt
on la.lease_term_id = lt.id and lt.is_deleted = 0
<where>
la.is_deleted = 0
<if test="queryVo.provinceId != null">
and ai.province_id = #{queryVo.provinceId}
</if>
<if test="queryVo.cityId != null">
and ai.city_id = #{queryVo.cityId}
</if>
<if test="queryVo.districtId != null">
and ai.district_id = #{queryVo.districtId}
</if>
<if test="queryVo.apartmentId != null">
and la.apartment_id = #{queryVo.apartmentId}
</if>
<if test="queryVo.roomNumber != null and queryVo.roomNumber != ''">
and ri.room_number like concat('%',#{queryVo.roomNumber},'%')
</if>
<if test="queryVo.name != null and queryVo.name != ''">
and la.name like concat('%',#{queryVo.name},'%')
</if>
<if test="queryVo.phone != null and queryVo.phone != ''">
and la.phone like concat('%',#{queryVo.phone},'%')
</if>
</where>
</select>

根据ID查询租约信息

Alt+Enter,实现方法
java
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement>
implements LeaseAgreementService {
@Autowired
private LeaseAgreementMapper leaseAgreementMapper;
@Autowired
private ApartmentInfoMapper apartmentInfoMapper;
@Autowired
private RoomInfoMapper roomInfoMapper;
@Autowired
private PaymentTypeMapper paymentTypeMapper;
@Autowired
private LeaseTermMapper leaseTermMapper;
@Override
public IPage<AgreementVo> pageAgreementByQuery(Page<AgreementVo> page, AgreementQueryVo queryVo) {
return leaseAgreementMapper.pageAgreementByQuery(page, queryVo);
}
@Override
public AgreementVo getAgreementById(Long id) {
//1.查询租约信息
LeaseAgreement leaseAgreement = leaseAgreementMapper.selectById(id);
//2.查询公寓信息
ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(leaseAgreement.getApartmentId());
//3.查询房间信息
RoomInfo roomInfo = roomInfoMapper.selectById(leaseAgreement.getRoomId());
//4.查询支付方式
PaymentType paymentType = paymentTypeMapper.selectById(leaseAgreement.getPaymentTypeId());
//5.查询租期
LeaseTerm leaseTerm = leaseTermMapper.selectById(leaseAgreement.getLeaseTermId());
//6.组合
AgreementVo agreementVo = new AgreementVo();
BeanUtils.copyProperties(leaseAgreement, agreementVo);
agreementVo.setApartmentInfo(apartmentInfo);
agreementVo.setRoomInfo(roomInfo);
agreementVo.setPaymentType(paymentType);
agreementVo.setLeaseTerm(leaseTerm);
return agreementVo;
}
}

根据ID删除租约信息

成功

根据ID更新租约状态

成功


定时检查租约状态



4.用户管理
java
@Autowired
private UserInfoService userInfoService;
根据条件分页查询用户列表
条件:手机号码、用户账号状态

java
LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(queryVo.getPhone()!= null,UserInfo::getPhone, queryVo.getPhone());
queryWrapper.eq(queryVo.getStatus()!= null,UserInfo::getStatus, queryVo.getStatus());
分页查询
java
Page<UserInfo> page = new Page<>(current, size);
...
Page<UserInfo> list = userInfoService.page(page, queryWrapper);
return Result.ok(list);
测试

根据ID更新用户状态
java
@Operation(summary = "根据用户id更新账号状态")
@PostMapping("updateStatusById")
public Result updateStatusById(@RequestParam Long id, @RequestParam BaseStatus status) {
LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(UserInfo::getId, id);
updateWrapper.set(UserInfo::getStatus, status);
userInfoService.update(updateWrapper);
return Result.ok();
}


5.系统管理
后台用户岗位管理
SystemPostController.java
java
@Autowired
private SystemPostService systemPostService;
分页查询岗位信息
java
@Operation(summary = "分页获取岗位信息")
@GetMapping("page")
private Result<IPage<SystemPost>> page(@RequestParam long current, @RequestParam long size) {
IPage<SystemPost> page = new Page<>(current, size);
IPage<SystemPost> list = systemPostService.page(page);
return Result.ok(list);
}
测试

保存或更新岗位信息
java
@Operation(summary = "保存或更新岗位信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody SystemPost systemPost) {
systemPostService.saveOrUpdate(systemPost);
return Result.ok();
}


根据ID删除岗位信息
java
@DeleteMapping("deleteById")
@Operation(summary = "根据id删除岗位")
public Result removeById(@RequestParam Long id) {
systemPostService.removeById(id);
return Result.ok();
}
测试

获取全部岗位列表
java
@Operation(summary = "获取全部岗位列表")
@GetMapping("list")
public Result<List<SystemPost>> list() {
List<SystemPost> list = systemPostService.list();
return Result.ok(list);
}

根据ID获取岗位信息
java
@GetMapping("getById")
@Operation(summary = "根据id获取岗位信息")
public Result<SystemPost> getById(@RequestParam Long id) {
SystemPost systemPost = systemPostService.getById(id);
return Result.ok(systemPost);
}

根据ID修改岗位状态
java
@Operation(summary = "根据岗位id修改状态")
@PostMapping("updateStatusByPostId")
public Result updateStatusByPostId(@RequestParam Long id, @RequestParam BaseStatus status) {
LambdaUpdateWrapper<SystemPost> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SystemPost::getId, id)
.set(SystemPost::getStatus, status);
return Result.ok();
}


后台用户信息管理
java
@Autowired
private SystemUserService systemUserService;
根据条件分页查询后台用户列表
用户和岗位表
java
@Operation(summary = "根据条件分页查询后台用户列表")
@GetMapping("page")
public Result<IPage<SystemUserItemVo>> page(@RequestParam long current, @RequestParam long size, SystemUserQueryVo queryVo) {
IPage<SystemUser> page = new Page<>(current, size);
IPage<SystemUserItemVo> list = systemUserService.pageSystemUserByQuery(page, queryVo);
return Result.ok(list);
}
alt+enter、实现方法
SystemUserServiceImpl.java
java
@Autowired
private SystemUserMapper systemUserMapper;
@Override
public IPage<SystemUserItemVo> pageSystemUserByQuery(IPage<SystemUser> page, SystemUserQueryVo queryVo) {
return systemUserMapper.pageSystemUserByQuery(page, queryVo);
}
alt+enter、Generate statement
java
<select id="pageSystemUserByQuery"
resultType="org.example.lease.web.admin.vo.system.user.SystemUserItemVo">
select su.id,
username,
su.name,
type,
phone,
avatar_url,
additional_info,
post_id,
su.status,
sp.name post_name
from system_user su
left join system_post sp on su.post_id = sp.id and sp.is_deleted = 0
<where>
su.is_deleted = 0
<if test="queryVo.name != null and queryVo.name != ''">
and su.name like concat('%',#{queryVo.name},'%')
</if>
<if test="queryVo.phone !=null and queryVo.phone != ''">
and su.phone like concat('%',#{queryVo.phone},'%')
</if>
</where>
</select>
测试

根据ID查询后台用户信息
java
@Operation(summary = "根据ID查询后台用户信息")
@GetMapping("getById")
public Result<SystemUserItemVo> getById(@RequestParam Long id) {
SystemUser list = systemUserService.getSystemUserById(id);
return Result.ok();
}
alt+enter、实现方法
SystemUserServiceImpl.java
java
/**
* 根据用户ID获取系统用户信息
*
* @param id 用户ID
* @return SystemUserItemVo 包含用户信息的视图对象
*/
@Override
public SystemUserItemVo getSystemUserById(Long id) {
// 通过ID查询系统用户信息
SystemUser systemUser = systemUserMapper.selectById(id);
// 根据用户中的职位ID查询职位信息
SystemPost systemPost = systemPostMapper.selectById(systemUser.getPostId());
// 创建用户信息视图对象
SystemUserItemVo systemUserItemVo = new SystemUserItemVo();
// 将职位信息复制到用户信息视图对象中
BeanUtils.copyProperties(systemPost, systemUserItemVo);
// 设置职位名称
systemUserItemVo.setPostName(systemUserItemVo.getPostName());
// 返回包含用户信息的视图对象
return systemUserItemVo;
}

保存或更新后台用户信息
密码通常会使用一些单向函数进行处理,常用于处理密码的单向函数(算法)有MD5、SHA-256等
Apache Commons是Apache软件基金会下的一个项目,其致力于提供可重用的开源软件,其中包含了很多易于使用的现成工具。
Apache Commons 提供了一个工具类DigestUtils
在common模块的pom.xml中添加启动器
java
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
java
DigestUtils.md5Hex(systemUser.getPassword())
DigestUtils: Apache Commons Codec 提供的工具类,用于计算消息摘要(哈希值)md5Hex(): 方法将输入字符串转换为32位的小写MD5哈希值systemUser.getPassword(): 获取用户的密码
java
systemUser.setPassword()// 设置加密后的密码
整体
java
@Operation(summary = "保存或更新后台用户信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody SystemUser systemUser) {
if (systemUser.getPassword() != null) {
systemUser.setPassword(DigestUtils.md5Hex(systemUser.getPassword()));
}
systemUserService.saveOrUpdate(systemUser);
return Result.ok();
}
Mybatis-Plus 更新策略
使用Mybatis-Plus提供的更新方法时,若实体中的字段为null,默认情况下,最终生成的update语句中,不会包含该字段。若想改变默认行为,可做以下配置:
全局配置
在application.yml中配置如下参数
mybatis-plus:
global-config:
db-config:
update-strategy: <strategy>
-
ignore:忽略空值判断,不管字段是否为空,都会进行更新 -
not_null:进行非空判断,字段非空才会进行判断,默认值 -
not_empty:进行非空判断,并进行非空串("")判断,主要针对字符串类型 -
never:从不进行更新,不管该字段为何值,都不更新 -
strategy:可选值
局部配置
-
在实体类中的具体字段通过
@TableField注解进行配置,如下:@Schema(description = "密码") @TableField(value = "password", updateStrategy = FieldStrategy.NOT_EMPTY) private String password;


添加成功
判断后台用户名是否可用
java
@Operation(summary = "判断后台用户名是否可用")
@GetMapping("isUserNameAvailable")
public Result<Boolean> isUsernameExists(@RequestParam String username) {
LambdaQueryWrapper<SystemUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SystemUser::getUsername, username);
long count = systemUserService.count(queryWrapper);
return Result.ok(count == 0);
}


根据ID删除后台用户信息
java
@DeleteMapping("deleteById")
@Operation(summary = "根据ID删除后台用户信息")
public Result removeById(@RequestParam Long id) {
systemUserService.removeById(id);
return Result.ok();
}

根据ID修改后台用户状态
java
@Operation(summary = "根据ID修改后台用户状态")
@PostMapping("updateStatusByUserId")
public Result updateStatusByUserId(@RequestParam Long id, @RequestParam BaseStatus status) {
LambdaUpdateWrapper<SystemUser> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SystemUser::getId, id).set(SystemUser::getStatus, status);
systemUserService.update(updateWrapper);
return Result.ok();
}

6.登录管理
接口开发
LoginController.java
java
@Autowired
private LoginService loginService;
获取图形验证码
验证码生成工具, 使用开源的验证码生成工具EasyCaptcha
在common模块的pom.xml文件中增加如下启动器
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
</dependency>
Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.yml中增加如下配置
spring:
data:
redis:
host: <hostname>
port: <port>
database: 0
LoginController.java
java
@Operation(summary = "获取图形验证码")
@GetMapping("login/captcha")
public Result<CaptchaVo> getCaptcha() {
CaptchaVo captcha = loginService.getCaptcha();
return Result.ok(captcha);
}
Alt+enter、实现方法
LoginServiceImpl.java
java
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public CaptchaVo getCaptcha() {
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
String code = specCaptcha.text().toLowerCase();
String key = "admin:login:" + UUID.randomUUID();
String image = specCaptcha.toBase64();
redisTemplate.opsForValue().set(key, code, 60, TimeUnit.SECONDS);
return new CaptchaVo(image, key);
}



为方便管理,可以将Reids相关的一些值定义为常量,例如key的前缀、TTL时长
common/src/main/java/org/example/lease/common/constant/RedisConstant.java
java
public class RedisConstant {
public static final String ADMIN_LOGIN_PREFIX = "admin:login:";
public static final Integer ADMIN_LOGIN_CAPTCHA_TTL_SEC = 60;
public static final String APP_LOGIN_PREFIX = "app:login:";
public static final Integer APP_LOGIN_CODE_RESEND_TIME_SEC = 60;
public static final Integer APP_LOGIN_CODE_TTL_SEC = 60 * 10;
public static final String APP_ROOM_PREFIX = "app:room:";
}
LoginServiceImpl.java
java
@Override
public CaptchaVo getCaptcha() {
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
String code = specCaptcha.text().toLowerCase();
String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();
String image = specCaptcha.toBase64();
redisTemplate.opsForValue().set(key, code, RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);
return new CaptchaVo(image, key);
}
登录接口
- 前端发送
username、password、captchaKey、captchaCode请求登录。


LoginController.java
java
@Operation(summary = "登录")
@PostMapping("login")
public Result<String> login(@RequestBody LoginVo loginVo) {
String token = loginService.login(loginVo);
return Result.ok(token);
}
alt+enter、实现方法
LoginServiceImpl.java
- 判断
captchaCode是否为空,若为空,则直接响应验证码为空;若不为空进行下一步判断。
java
//1.判断是否输入了验证码
if (loginVo.getCaptchaCode() == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);
}
- 根据
captchaKey从Redis中查询之前保存的code,若查询出来的code为空,则直接响应验证码已过期;若不为空进行下一步判断。
java
//2.校验验证码
String code = redisTemplate.opsForValue().get(loginVo.getCaptchaKey());
if (code == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);
}
- 比较
captchaCode和code,若不相同,则直接响应验证码不正确;若相同则进行下一步判断。
java
if (!code.equals(loginVo.getCaptchaCode().toLowerCase())) {
throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);
}
java
@Autowired
private SystemUserMapper systemUserMapper;
- 根据
username查询数据库,若查询结果为空,则直接响应账号不存在;若不为空则进行下一步判断。
java
//3.校验用户是否存在
LambdaQueryWrapper<SystemUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SystemUser::getUsername, loginVo.getUsername());
SystemUser systemUser = systemUserMapper.selectOne(queryWrapper);
if (systemUser == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR);
}
- 查看用户状态,判断是否被禁用,若禁用,则直接响应
账号被禁;若未被禁用,则进行下一步判断。
java
//4.校验用户是否被禁
if (systemUser.getStatus() == BaseStatus.DISABLE) {
throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);
}
- 比对
password和数据库中查询的密码,若不一致,则直接响应账号或密码错误,若一致则进行入最后一步。
java
//5.校验用户密码
if (!systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))) {
throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);
}
- 创建JWT,并响应给浏览器。
引入Maven依赖
在common模块的pom.xml文件中增加启动器
java
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
common/src/main/java/org/example/lease/common/utils/JwtUtil.java
java
public static String creatToken(Long userId, String username) {
SecretKey secretKey = Keys.hmacShaKeyFor("asdfghjkl".getBytes());
String token = Jwts.builder()
.setSubject("USER_INFO")
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.claim("userId", userId)
.claim("username", username)
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
return token;
}
测试
java
public static void main(String[] args) {
System.out.println(creatToken(1l, "zhangsan"));
}
JWT签名密钥的长度不符合安全要求
随机生成密码

LoginServiceImpl.java
java
return JwtUtil.creatToken(systemUser.getId(), systemUser.getUsername());
测试

原因

修改方法

alt+enter、Generate statement
java
select id,
username,
password,
name,
type,
phone,
avatar_url,
additional_info,
post_id,
status
from system_user
where is_deleted = 0
and username = #{username}
测试

编写HandlerInterceptor
我们需要为所有受保护的接口增加校验JWT合法性的逻辑。
JwtUtil.java
java
public static void parseToken(String token) {
try {
JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
jwtParser.parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);
} catch (JwtException e) {
throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);
}
}
在web-admin模块中创键
web/web-admin/src/main/java/org/example/lease/web/admin/custom/interceptor/AuthenticationInterceptor.java
java
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("access-token");
if (token == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
}
JwtUtil.parseToken(token);
return true;
}
}
java
if (token == null) {
throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
}
移到JwtUtil.java
Knife4j配置
在增加上述拦截器后,为方便继续调试其他接口,可以获取一个长期有效的Token,将其配置到Knife4j的全局参数中
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.authenticationInterceptor);
}
测试

控制拦截器
WebMvcConfiguration.java
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**");
}
测试

控制拦截器
WebMvcConfiguration.java
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/login/**");
}
测试


发送请求时会自动携带该header
生成测试用jwt
JwtUtil.java
java
public static void main(String[] args) {
System.out.println(creatToken(2L, "user"));
}
java
.setExpiration(new Date(System.currentTimeMillis() + 3600000*24*365L))


测试

需要每个分组设置全局参数
成功

获取登录用户个人信息
需要从Jwt中获取用户id

JwtUtil.java

LoginController.java
java
@Operation(summary = "获取登陆用户个人信息")
@GetMapping("info")
public Result<SystemUserInfoVo> info(@RequestHeader("access-token") String token) {
Claims claims = JwtUtil.parseToken(token);
Long userId = claims.get("userId", Long.class);
SystemUserInfoVo userInfo = loginService.getLoginUserInfoById(userId);
return Result.ok(userInfo);
}

alt+enter、实现方法
java
@Override
public SystemUserInfoVo getLoginUserInfoById(Long userId) {
SystemUser systemUser = systemUserMapper.selectById(userId);
SystemUserInfoVo systemUserInfoVo = new SystemUserInfoVo();
systemUserInfoVo.setName(systemUser.getName());
systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());
return systemUserInfoVo;
}

JWT会被重复解析两次,在拦截器将Token解析完毕后,将结果保存至ThreadLocal中
在common模块中创建common/src/main/java/org/example/lease/common/login/LoginUserHolder.java
java
package org.example.lease.common.login;
public class LoginUserHolder {
public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
public static void setLoginUser(LoginUser loginUser) {
threadLocal.set(loginUser);
}
public static LoginUser getLoginUser() {
return threadLocal.get();
}
public static void clear() {
threadLocal.remove();
}
}
common/src/main/java/org/example/lease/common/login/LoginUser.java
java
package org.example.lease.common.login;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class LoginUser {
private Long userId;
private String username;
}
修改AuthenticationInterceptor拦截器
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("access-token");
Claims clamis = JwtUtil.parseToken(token);
Long userId = clamis.get("userId", Long.class);
String username = clamis.get("username", String.class);
LoginUserHolder.setLoginUser(new LoginUser(userId, username));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LoginUserHolder.clear();
}
LoginController.java
java
@Operation(summary = "获取登陆用户个人信息")
@GetMapping("info")
public Result<SystemUserInfoVo> info() {
Long userId = LoginUserHolder.getLoginUser().getUserId();
SystemUserInfoVo userInfo = loginService.getLoginUserInfoById(userId);
return Result.ok(userInfo);
}
