设备列表 视频流服务器
This commit is contained in:
@@ -2,19 +2,25 @@ package org.dromara.system.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.SystemConstants;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.enums.UserType;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import org.dromara.common.mybatis.utils.IdGeneratorUtil;
|
||||
import org.dromara.system.api.AppUserApiService;
|
||||
import org.dromara.system.api.domain.AppUserDTO;
|
||||
import org.dromara.system.api.domain.AppUserQueryDTO;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.mapper.SysUserMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* APP 用户领域服务({@link AppUserApiService})实现。
|
||||
@@ -240,6 +246,13 @@ public class AppUserApiServiceImpl implements AppUserApiService {
|
||||
|
||||
/**
|
||||
* 重置用户密码(忘记密码场景);新密码在本方法内部散列后落库。
|
||||
* <p>
|
||||
* <b>限定 {@code user_type = app_user}</b>(2026-09-17 加固):本方法原先只按 {@code userId} 更新,
|
||||
* 若被后台管理端以任意主键调用,即可重置<b>后台账号</b>密码——属越权写入。
|
||||
* 现补上类型条件,使「重置密码」这一高危动作的爆炸半径限定在 APP 用户内。
|
||||
* <p>
|
||||
* 对既有调用方<b>无行为影响</b>:三处调用(手机号找回 / 邮箱找回 / APP 内改密)的 {@code userId}
|
||||
* 均取自 {@code selectByPhone} / {@code selectByEmail} / 登录态,本就恒为 app_user。
|
||||
*
|
||||
* @param userId 用户主键
|
||||
* @param rawPassword 新明文密码
|
||||
@@ -253,6 +266,7 @@ public class AppUserApiServiceImpl implements AppUserApiService {
|
||||
return userMapper.lambda()
|
||||
.set(SysUser::getPassword, BCrypt.hashpw(rawPassword))
|
||||
.eq(SysUser::getUserId, userId)
|
||||
.eq(SysUser::getUserType, UserType.APP_USER.getUserType())
|
||||
.updateCount() > 0;
|
||||
}
|
||||
|
||||
@@ -312,4 +326,107 @@ public class AppUserApiServiceImpl implements AppUserApiService {
|
||||
return user != null && StringUtils.isNotBlank(user.getPhoneNumber());
|
||||
}
|
||||
|
||||
// ---------------- 后台管理端(APP 用户管理) ----------------
|
||||
|
||||
/**
|
||||
* 分页查询 APP 用户列表(后台「APP 用户管理」用)。
|
||||
* <p>
|
||||
* <b>强制 {@code user_type = app_user}</b>:该条件由本方法固定附加,
|
||||
* {@link AppUserQueryDTO} 中也没有对应字段可覆盖,因此结果集恒不含后台账号。
|
||||
* <p>
|
||||
* <b>为什么刻意不加 {@code @DataPermission}</b>:APP 账号注册时不写 {@code dept_id}
|
||||
* (见 {@link #registerByPhone}/{@link #registerByEmail}),该列恒为 {@code NULL};
|
||||
* 若叠加框架按部门维度的数据权限过滤,会把全部 APP 用户过滤成空集。
|
||||
* APP 用户本就游离于后台组织架构之外,故此处按「业务归属正确」优先。
|
||||
* <p>
|
||||
* <b>排序</b>:按注册时间倒序(新注册的排前面),与后台「看最新」的使用习惯一致。
|
||||
*
|
||||
* @param query 查询条件(关键词 / 状态 / 注册时间区间 / 分页)
|
||||
* @return APP 用户分页结果(不含密码)
|
||||
*/
|
||||
@Override
|
||||
public PageResult<AppUserDTO> selectAppUserPage(AppUserQueryDTO query) {
|
||||
AppUserQueryDTO condition = query == null ? new AppUserQueryDTO() : query;
|
||||
int pageNum = (condition.getPageNum() == null || condition.getPageNum() < 1) ? 1 : condition.getPageNum();
|
||||
int pageSize = (condition.getPageSize() == null || condition.getPageSize() < 1) ? 10 : condition.getPageSize();
|
||||
|
||||
String keyword = condition.getKeyword();
|
||||
LambdaQueryWrapper<SysUser> wrapper = QueryBuilder.lambda(SysUser.class)
|
||||
.eq(SysUser::getUserType, UserType.APP_USER.getUserType())
|
||||
.and(StringUtils.isNotBlank(keyword), w -> w
|
||||
.like(SysUser::getUserName, keyword)
|
||||
.or().like(SysUser::getNickName, keyword)
|
||||
.or().like(SysUser::getPhoneNumber, keyword)
|
||||
.or().like(SysUser::getEmail, keyword))
|
||||
.eqIfText(SysUser::getStatus, condition.getStatus())
|
||||
.betweenParams(SysUser::getCreateTime, condition.getParams(), "beginTime", "endTime")
|
||||
.orderByDesc(SysUser::getCreateTime)
|
||||
.build();
|
||||
|
||||
Page<SysUser> page = userMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
|
||||
List<AppUserDTO> rows = page.getRecords().stream()
|
||||
.map(user -> BeanUtil.copyProperties(user, AppUserDTO.class))
|
||||
.toList();
|
||||
return PageResult.build(rows, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按主键查询 APP 用户详情(后台「APP 用户管理」用)。
|
||||
* <p>
|
||||
* <b>强制 {@code user_type = app_user}</b>:传入后台账号主键时返回 {@code null},
|
||||
* 使管理端无法借该接口读到系统账号信息(越界读取防护)。
|
||||
*
|
||||
* @param userId 用户主键
|
||||
* @return APP 用户信息(不含密码);不存在或非 app_user 时返回 {@code null}
|
||||
*/
|
||||
@Override
|
||||
public AppUserDTO selectAppUserById(Long userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
SysUser user = userMapper.lambda()
|
||||
.eq(SysUser::getUserId, userId)
|
||||
.eq(SysUser::getUserType, UserType.APP_USER.getUserType())
|
||||
.one();
|
||||
return user == null ? null : BeanUtil.copyProperties(user, AppUserDTO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 APP 用户账号状态(停用 / 启用)。
|
||||
* <p>
|
||||
* <b>为什么同时限定 {@code user_type}</b>:本方法来自后台管理端(有登录态),
|
||||
* 若不限定类型,构造任意 {@code userId} 即可停用后台账号——属于典型的越权写入。
|
||||
* 加上该条件后,「非 APP 账号」的 update 影响行数为 0,天然被拒。
|
||||
* <p>
|
||||
* 返回 {@code true} 表示「目标状态已生效」,包含「原本就是该状态」的情形
|
||||
* ({@code updateCount} 为 0 但语义上无需变更),便于前端幂等重试。
|
||||
*
|
||||
* @param userId 用户主键
|
||||
* @param status 目标状态({@code 0} 正常 / {@code 1} 停用)
|
||||
* @return true 目标状态已生效;false 目标用户不存在或非 APP 账号
|
||||
*/
|
||||
@Override
|
||||
public boolean updateStatus(Long userId, String status) {
|
||||
if (userId == null || StringUtils.isBlank(status)) {
|
||||
return false;
|
||||
}
|
||||
// 先确认目标确为 APP 账号,避免「影响行数 0」被误判为「已是目标状态」
|
||||
SysUser exist = userMapper.lambda()
|
||||
.select(SysUser::getUserId, SysUser::getStatus)
|
||||
.eq(SysUser::getUserId, userId)
|
||||
.eq(SysUser::getUserType, UserType.APP_USER.getUserType())
|
||||
.one();
|
||||
if (exist == null) {
|
||||
return false;
|
||||
}
|
||||
if (Objects.equals(exist.getStatus(), status)) {
|
||||
return true;
|
||||
}
|
||||
return userMapper.lambda()
|
||||
.set(SysUser::getStatus, status)
|
||||
.eq(SysUser::getUserId, userId)
|
||||
.eq(SysUser::getUserType, UserType.APP_USER.getUserType())
|
||||
.updateCount() > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user