feat:导入用户防抖

fix:删除用户bug修改
This commit is contained in:
dzc committed 2026-08-11 10:28:41 +08:00
1 parent f86a3b9f28
commit 99341f509b
4 files changed
+99 -14

No files matched your search

@@ -0,0 +1,27 @@
package com.bsd.pjcloud.admin.api.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 删除用户请求项。
* <p>流程信息来自用户列表快照,后端删除时仍以数据库中的实时审批状态和流程ID为准。</p>
*/
@Data
@Schema(description = "删除用户请求项")
public class SysUserDeleteDto {
/**
* 待删除的系统用户ID。
*/
@NotNull(message = "用户ID不能为空")
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
/**
* 前端列表中的审批流程实例ID快照;允许为空,不作为后端取消流程的可信数据源。
*/
@Schema(description = "审批流程实例ID快照")
private String processId;
}
@@ -27,6 +27,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.admin.api.dto.ForgotPasswordDTO;
import com.bsd.pjcloud.admin.api.dto.SysUserDeleteDto;
import com.bsd.pjcloud.admin.api.dto.UserDTO;
import com.bsd.pjcloud.admin.api.dto.UserDeptSimpleInfo;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
@@ -45,6 +46,7 @@ import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.excel.annotation.RequestExcel;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.HasPermission;
import com.bsd.pjcloud.common.security.annotation.Inner;
@@ -247,17 +249,24 @@ public class SysUserController {
}
/**
* 删除用户信息
* 删除用户信息;审核中的用户会先取消当前审批流程。
*
* @param ids ID
* @return R
* @param users 待删除用户及其前端流程快照
* @return 删除结果;请求为空或删除前置校验失败时返回失败信息
*/
@SysLog("删除用户信息")
@DeleteMapping
@HasPermission("sys_user_del")
@Operation(summary = "删除用户", description = "根据ID删除用户")
public R userDel(@RequestBody Long[] ids) {
return R.ok(userService.deleteUserByIds(ids));
@Operation(summary = "删除用户", description = "根据ID删除用户,审核中的用户会先取消审批流程")
public R<Boolean> userDel(@Valid @RequestBody List<SysUserDeleteDto> users) {
if (CollUtil.isEmpty(users)) {
return R.failed("请选择要删除的用户");
}
if (users.stream().anyMatch(user -> user == null || user.getUserId() == null)) {
return R.failed("用户ID不能为空");
}
Long[] userIds = users.stream().map(SysUserDeleteDto::getUserId).distinct().toArray(Long[]::new);
return R.ok(userService.deleteUserByIds(userIds));
}
/**
@@ -411,16 +420,30 @@ public class SysUserController {
}
/**
* 导入学生用户
* 导入学生用户。
*
* @param studentUserExcelList Excel 中解析出的学生用户数据
* @param bindingResult Excel 通用校验产生的错误信息
* @return 导入结果;失败时包含对应行号和错误原因
*/
@PostMapping("/import-student")
@HasPermission("sys_student_user_export")
@Idempotent(expireTime = 5, key = "'/user/import-student:' + #studentUserExcelList.hashCode() + ':' + T(com.bsd.pjcloud.common.security.util.SecurityUtils).getUser().getId()", info = "正在导入中,请勿重复操作")
public R importStudentUser(@RequestExcel List<StudentUserExcelVO> studentUserExcelList, BindingResult bindingResult) {
return userService.importStudentUser(studentUserExcelList, bindingResult);
}
/**
* 导入用户及 Excel 中内嵌的人脸图片。
*
* @param file 原始 Excel 文件,用于读取内嵌的人脸图片
* @param excelList Excel 中解析出的用户数据
* @param bindingResult Excel 通用校验产生的错误信息
* @return 导入结果;失败时包含校验错误或人脸图片处理错误
*/
@PostMapping("/import-face")
@HasPermission("sys_user_export")
@Idempotent(expireTime = 5, key = "'/user/import-face:' + #file.getSize() + ':' + #excelList.hashCode() + ':' + T(com.bsd.pjcloud.common.security.util.SecurityUtils).getUser().getId()", info = "正在导入中,请勿重复操作")
public R importUserFace(@RequestParam("file") MultipartFile file, @RequestExcel List<UserExcelFaceVo> excelList, BindingResult bindingResult) {
return userService.importUserFace(file, excelList, bindingResult);
}
@@ -64,10 +64,10 @@ public interface SysUserService extends IService<SysUser> {
IPage<UserVO> innerPage(Page page, UserDTO userDTO);
/**
* 删除用户
* 删除指定用户,并在删除前取消其中仍处于审核中的审批流程。
*
* @param ids 用户
* @return boolean
* @param ids 待删除的用户ID数组,不能为空
* @return 用户及其关联数据进入删除流程后返回 {@code true}
*/
Boolean deleteUserByIds(Long[] ids);
@@ -79,9 +79,11 @@ import com.bsd.pjcloud.common.data.tenant.TenantContextHolder;
import com.bsd.pjcloud.common.excel.vo.ErrorMessage;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.flow.task.api.feign.RemoteFlowEngineService;
import com.bsd.pjcloud.flow.task.constant.TaskCheckStatusEnum;
import com.bsd.pjcloud.flow.task.dto.ProcessNodeRecordAssignUserParamDto;
import com.bsd.pjcloud.flow.task.dto.TaskDto;
import com.bsd.pjcloud.flow.task.dto.TaskParamDto;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletService;
import cn.hutool.extra.spring.SpringUtil;
@@ -172,6 +174,11 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
private final RemoteWalletService remoteWalletService;
/**
* 审批流程远程服务,用于在删除审核中用户前终止对应流程实例。
*/
private final RemoteFlowEngineService remoteFlowEngineService;
private final SysFileService sysFileService;
private final FlowUtil flowUtil;
@@ -403,16 +410,23 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
/**
* 删除用户
* 删除用户,并在删除前终止仍处于审核中的用户审批流程。
* <p>审批状态和流程ID以数据库实时数据为准;流程取消失败时抛出异常并停止本次删除。</p>
*
* @param ids 用户ID 列表
* @return Boolean
* @return 所有用户及其关联数据进入删除流程后返回 {@code true}
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteUserByIds(Long[] ids) {
if (ArrayUtil.isEmpty(ids)) {
throw new RuntimeException("请选择要删除的用户");
}
List<Long> userIds = CollUtil.toList(ids);
List<SysUser> userList = baseMapper.selectBatchIds(userIds);
if (userList.size() != userIds.stream().distinct().count()) {
throw new RuntimeException("部分用户不存在或已删除,请刷新后重试");
}
// 判断是否可以删除
userList.forEach(v -> {
String errorMsg = remoteWalletService.isDel(v.getUserId()).getData();
@@ -421,6 +435,27 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
});
// 只终止数据库中仍为审核中的流程;前端携带的流程ID可能已经过期,不能作为删除依据。
List<String> pendingProcessIds = new ArrayList<>();
for (SysUser user : userList) {
if (!ObjectUtil.equals(user.getProcessStatus(), TaskCheckStatusEnum.PADDING.getCode())) {
continue;
}
if (StrUtil.isBlank(user.getProcessId())) {
throw new RuntimeException("审核中的用户缺少流程ID,无法删除");
}
pendingProcessIds.add(user.getProcessId());
}
if (CollUtil.isNotEmpty(pendingProcessIds)) {
TaskParamDto taskParam = new TaskParamDto();
taskParam.setProcessInstanceIdList(pendingProcessIds.stream().distinct().toList());
R<String> stopResult = remoteFlowEngineService.stopProcessInstance(taskParam);
if (stopResult == null || !stopResult.isOk()) {
String errorMessage = stopResult == null ? null : stopResult.getMsg();
throw new RuntimeException(StrUtil.blankToDefault(errorMessage, "取消用户审批流程失败"));
}
}
// 删除 spring cache
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
for (SysUser sysUser : userList) {
@@ -435,9 +470,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
this.removeBatchByIds(userIds);
// 删除钱包
CompletableFuture.runAsync(() -> {
remoteWalletService.delWallet(List.of(ids));
remoteWalletService.delWallet(userIds);
});
for (Long userId : ids) {
for (Long userId : userIds) {
remoteWalletService.userChange(userId, Boolean.TRUE);
}