From 99341f509b892e34ed6f5b5d2c037ffc0ed8af7a Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Tue, 11 Aug 2026 10:28:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=AF=BC=E5=85=A5=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E9=98=B2=E6=8A=96=20fix:=E5=88=A0=E9=99=A4=E7=94=A8=E6=88=B7bu?= =?UTF-8?q?g=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/api/dto/SysUserDeleteDto.java | 27 ++++++++++++ .../admin/controller/SysUserController.java | 37 +++++++++++++--- .../pjcloud/admin/service/SysUserService.java | 6 +-- .../service/impl/SysUserServiceImpl.java | 43 +++++++++++++++++-- 4 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/SysUserDeleteDto.java diff --git a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/SysUserDeleteDto.java b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/SysUserDeleteDto.java new file mode 100644 index 000000000..3db286107 --- /dev/null +++ b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/SysUserDeleteDto.java @@ -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; + +/** + * 删除用户请求项。 + *

流程信息来自用户列表快照,后端删除时仍以数据库中的实时审批状态和流程ID为准。

+ */ +@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; +} diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java index 1f63663ee..db6671885 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java @@ -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 userDel(@Valid @RequestBody List 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 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 excelList, BindingResult bindingResult) { return userService.importUserFace(file, excelList, bindingResult); } diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java index 6e2394041..36da87b2e 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java @@ -64,10 +64,10 @@ public interface SysUserService extends IService { IPage innerPage(Page page, UserDTO userDTO); /** - * 删除用户 + * 删除指定用户,并在删除前取消其中仍处于审核中的审批流程。 * - * @param ids 用户 - * @return boolean + * @param ids 待删除的用户ID数组,不能为空 + * @return 用户及其关联数据进入删除流程后返回 {@code true} */ Boolean deleteUserByIds(Long[] ids); diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java index bcee9f2dc..f43cc0af2 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java @@ -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 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 impl } /** - * 删除用户 + * 删除用户,并在删除前终止仍处于审核中的用户审批流程。 + *

审批状态和流程ID以数据库实时数据为准;流程取消失败时抛出异常并停止本次删除。

* * @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 userIds = CollUtil.toList(ids); List 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 impl } }); + // 只终止数据库中仍为审核中的流程;前端携带的流程ID可能已经过期,不能作为删除依据。 + List 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 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 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); }