From 6e1bd73cd80b7244c89ec4309f5595a45cb931eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com> Date: Tue, 24 Mar 2026 18:37:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=89=B9=E9=87=8F=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增账号批量删除和批量重置密码接口及结果返回 - 用户列表增加批量操作工具栏与结果提示 - 账号删除切换为逻辑删除语义 --- .../system/SysAccountController.java | 23 ++ .../system/entity/base/SysAccountBase.java | 14 ++ .../vo/SysAccountBatchActionErrorItemVo.java | 39 +++ .../vo/SysAccountBatchActionResultVo.java | 50 ++++ .../system/service/SysAccountService.java | 6 + .../service/impl/SysAccountServiceImpl.java | 122 +++++++++- .../tech/easyflow/starter/MybatisConfig.java | 4 + .../V4__sys_account_logic_delete.sql | 6 + .../components/headerSearch/HeaderSearch.vue | 13 + .../src/locales/langs/en-US/sysAccount.json | 14 ++ .../src/locales/langs/zh-CN/sysAccount.json | 14 ++ .../system/sysAccount/SysAccountList.vue | 222 +++++++++++++++++- 12 files changed, 513 insertions(+), 14 deletions(-) create mode 100644 easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionErrorItemVo.java create mode 100644 easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionResultVo.java create mode 100644 easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/V4__sys_account_logic_delete.sql diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java index ad1b4e8..16397a7 100644 --- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java +++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java @@ -22,6 +22,7 @@ import tech.easyflow.common.web.controller.BaseCurdController; import tech.easyflow.common.web.jsonbody.JsonBody; import tech.easyflow.log.annotation.LogRecord; import tech.easyflow.system.entity.SysAccount; +import tech.easyflow.system.entity.vo.SysAccountBatchActionResultVo; import tech.easyflow.system.entity.vo.SysAccountImportResultVo; import tech.easyflow.system.service.SysAccountService; import tech.easyflow.system.util.SysPasswordPolicy; @@ -180,6 +181,28 @@ public class SysAccountController extends BaseCurdController removeBatchWithResult( + @JsonBody(value = "ids", required = true) List ids) { + if (ids == null || ids.isEmpty()) { + return Result.fail("ids不能为空", null); + } + return Result.ok(service.removeBatchWithResult(ids)); + } + + @PostMapping("/resetPasswordBatch") + @SaCheckPermission("/api/v1/sysAccount/save") + @LogRecord("批量重置用户密码") + public Result resetPasswordBatch( + @JsonBody(value = "ids", required = true) List ids) { + if (ids == null || ids.isEmpty()) { + return Result.fail("ids不能为空", null); + } + return Result.ok(service.resetPasswordBatch(ids, SaTokenUtil.getLoginAccount().getId())); + } + @PostMapping("/importExcel") @SaCheckPermission("/api/v1/sysAccount/save") public Result importExcel(MultipartFile file) { diff --git a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/base/SysAccountBase.java b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/base/SysAccountBase.java index 20264c8..589748c 100644 --- a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/base/SysAccountBase.java +++ b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/base/SysAccountBase.java @@ -115,6 +115,12 @@ public class SysAccountBase extends DateEntity implements Serializable { @Column(comment = "备注") private String remark; + /** + * 删除标识 + */ + @Column(value = "is_deleted", isLogicDelete = true, comment = "删除标识") + private BigInteger isDeleted; + public BigInteger getId() { return id; } @@ -251,4 +257,12 @@ public class SysAccountBase extends DateEntity implements Serializable { this.remark = remark; } + public BigInteger getIsDeleted() { + return isDeleted; + } + + public void setIsDeleted(BigInteger isDeleted) { + this.isDeleted = isDeleted; + } + } diff --git a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionErrorItemVo.java b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionErrorItemVo.java new file mode 100644 index 0000000..fa33542 --- /dev/null +++ b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionErrorItemVo.java @@ -0,0 +1,39 @@ +package tech.easyflow.system.entity.vo; + +import java.math.BigInteger; + +/** + * 用户批量操作失败项。 + */ +public class SysAccountBatchActionErrorItemVo { + + private BigInteger id; + + private String loginName; + + private String reason; + + public BigInteger getId() { + return id; + } + + public void setId(BigInteger id) { + this.id = id; + } + + public String getLoginName() { + return loginName; + } + + public void setLoginName(String loginName) { + this.loginName = loginName; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } +} diff --git a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionResultVo.java b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionResultVo.java new file mode 100644 index 0000000..d74e1c7 --- /dev/null +++ b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/entity/vo/SysAccountBatchActionResultVo.java @@ -0,0 +1,50 @@ +package tech.easyflow.system.entity.vo; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用户批量操作结果。 + */ +public class SysAccountBatchActionResultVo { + + private int successCount = 0; + + private int errorCount = 0; + + private int totalCount = 0; + + private List errorItems = new ArrayList<>(); + + public int getSuccessCount() { + return successCount; + } + + public void setSuccessCount(int successCount) { + this.successCount = successCount; + } + + public int getErrorCount() { + return errorCount; + } + + public void setErrorCount(int errorCount) { + this.errorCount = errorCount; + } + + public int getTotalCount() { + return totalCount; + } + + public void setTotalCount(int totalCount) { + this.totalCount = totalCount; + } + + public List getErrorItems() { + return errorItems; + } + + public void setErrorItems(List errorItems) { + this.errorItems = errorItems; + } +} diff --git a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/SysAccountService.java b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/SysAccountService.java index c899a5b..0b04eb5 100644 --- a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/SysAccountService.java +++ b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/SysAccountService.java @@ -4,10 +4,12 @@ import com.mybatisflex.core.service.IService; import org.springframework.web.multipart.MultipartFile; import tech.easyflow.common.entity.LoginAccount; import tech.easyflow.system.entity.SysAccount; +import tech.easyflow.system.entity.vo.SysAccountBatchActionResultVo; import tech.easyflow.system.entity.vo.SysAccountImportResultVo; import java.io.OutputStream; import java.math.BigInteger; +import java.util.Collection; /** * 用户表 服务层。 @@ -23,6 +25,10 @@ public interface SysAccountService extends IService { void resetPassword(BigInteger accountId, BigInteger operatorId); + SysAccountBatchActionResultVo removeBatchWithResult(Collection ids); + + SysAccountBatchActionResultVo resetPasswordBatch(Collection ids, BigInteger operatorId); + SysAccountImportResultVo importAccounts(MultipartFile file, LoginAccount loginAccount); void writeImportTemplate(OutputStream outputStream); diff --git a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/impl/SysAccountServiceImpl.java b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/impl/SysAccountServiceImpl.java index 824e587..599d592 100644 --- a/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/impl/SysAccountServiceImpl.java +++ b/easyflow-modules/easyflow-module-system/src/main/java/tech/easyflow/system/service/impl/SysAccountServiceImpl.java @@ -26,6 +26,8 @@ import tech.easyflow.system.entity.SysAccountRole; import tech.easyflow.system.entity.SysDept; import tech.easyflow.system.entity.SysPosition; import tech.easyflow.system.entity.SysRole; +import tech.easyflow.system.entity.vo.SysAccountBatchActionErrorItemVo; +import tech.easyflow.system.entity.vo.SysAccountBatchActionResultVo; import tech.easyflow.system.entity.vo.SysAccountImportErrorRowVo; import tech.easyflow.system.entity.vo.SysAccountImportResultVo; import tech.easyflow.system.mapper.SysAccountMapper; @@ -42,6 +44,7 @@ import java.io.OutputStream; import java.math.BigInteger; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -164,13 +167,7 @@ public class SysAccountServiceImpl extends ServiceImpl ids) { + List accountIds = normalizeAccountIds(ids); + SysAccountBatchActionResultVo result = initBatchActionResult(accountIds); + if (accountIds.isEmpty()) { + return result; + } + + for (BigInteger accountId : accountIds) { + SysAccount record = getById(accountId); + if (record == null) { + appendBatchError(result, accountId, null, "用户不存在"); + continue; + } + try { + validateRemoveAllowed(record); + executeInRowTransaction(() -> { + boolean success = removeById(accountId); + if (!success) { + throw new BusinessException("删除失败"); + } + }); + result.setSuccessCount(result.getSuccessCount() + 1); + } catch (Exception e) { + appendBatchError(result, accountId, record.getLoginName(), extractErrorMessage(e, "删除失败")); + } + } + result.setErrorCount(result.getErrorItems().size()); + return result; + } + + @Override + public SysAccountBatchActionResultVo resetPasswordBatch(Collection ids, BigInteger operatorId) { + List accountIds = normalizeAccountIds(ids); + SysAccountBatchActionResultVo result = initBatchActionResult(accountIds); + if (accountIds.isEmpty()) { + return result; + } + + for (BigInteger accountId : accountIds) { + SysAccount record = getById(accountId); + if (record == null) { + appendBatchError(result, accountId, null, "用户不存在"); + continue; + } + try { + executeInRowTransaction(() -> resetPassword(accountId, operatorId)); + result.setSuccessCount(result.getSuccessCount() + 1); + } catch (Exception e) { + appendBatchError(result, accountId, record.getLoginName(), extractErrorMessage(e, "重置密码失败")); + } + } + result.setErrorCount(result.getErrorItems().size()); + return result; + } + @Override public SysAccountImportResultVo importAccounts(MultipartFile file, LoginAccount loginAccount) { validateImportFile(file); @@ -418,9 +471,21 @@ public class SysAccountServiceImpl extends ServiceImpl normalizeAccountIds(Collection ids) { + if (ids == null || ids.isEmpty()) { + return Collections.emptyList(); + } + LinkedHashSet uniqueIds = new LinkedHashSet<>(); + for (BigInteger id : ids) { + if (id != null) { + uniqueIds.add(id); + } + } + return new ArrayList<>(uniqueIds); + } + + private SysAccountBatchActionResultVo initBatchActionResult(List accountIds) { + SysAccountBatchActionResultVo result = new SysAccountBatchActionResultVo(); + result.setTotalCount(accountIds.size()); + return result; } private List> buildImportHeadList() { diff --git a/easyflow-starter/easyflow-starter-all/src/main/java/tech/easyflow/starter/MybatisConfig.java b/easyflow-starter/easyflow-starter-all/src/main/java/tech/easyflow/starter/MybatisConfig.java index 1474856..25df633 100644 --- a/easyflow-starter/easyflow-starter-all/src/main/java/tech/easyflow/starter/MybatisConfig.java +++ b/easyflow-starter/easyflow-starter-all/src/main/java/tech/easyflow/starter/MybatisConfig.java @@ -16,6 +16,10 @@ public class MybatisConfig implements MyBatisFlexCustomizer { //开启审计功能 AuditManager.setAuditEnable(true); + // 统一使用标准 0/1 逻辑删除语义。 + flexGlobalConfig.setNormalValueOfLogicDelete(0); + flexGlobalConfig.setDeletedValueOfLogicDelete(1); + //取消控制台的 Banner 打印 flexGlobalConfig.setPrintBanner(false); diff --git a/easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/V4__sys_account_logic_delete.sql b/easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/V4__sys_account_logic_delete.sql new file mode 100644 index 0000000..12d46f0 --- /dev/null +++ b/easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/V4__sys_account_logic_delete.sql @@ -0,0 +1,6 @@ +ALTER TABLE `tb_sys_account` + ADD COLUMN `is_deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除标识:0未删除,1已删除' AFTER `remark`; + +ALTER TABLE `tb_sys_account` + DROP INDEX `uni_login_name`, + ADD INDEX `idx_login_name` (`login_name`) USING BTREE; diff --git a/easyflow-ui-admin/app/src/components/headerSearch/HeaderSearch.vue b/easyflow-ui-admin/app/src/components/headerSearch/HeaderSearch.vue index 33df232..a01af2c 100644 --- a/easyflow-ui-admin/app/src/components/headerSearch/HeaderSearch.vue +++ b/easyflow-ui-admin/app/src/components/headerSearch/HeaderSearch.vue @@ -115,6 +115,9 @@ const handleDropdownClick = (button) => { {{ $t('button.reset') }} +
+ +
@@ -193,6 +196,12 @@ const handleDropdownClick = (button) => { gap: 12px; } +.search-middle { + display: flex; + align-items: center; + min-width: 0; +} + .search-input { width: min(360px, 100%); } @@ -267,6 +276,10 @@ const handleDropdownClick = (button) => { justify-content: flex-start; } + .search-middle { + width: 100%; + } + .search-input { width: 100%; } diff --git a/easyflow-ui-admin/app/src/locales/langs/en-US/sysAccount.json b/easyflow-ui-admin/app/src/locales/langs/en-US/sysAccount.json index 71f087a..884e462 100644 --- a/easyflow-ui-admin/app/src/locales/langs/en-US/sysAccount.json +++ b/easyflow-ui-admin/app/src/locales/langs/en-US/sysAccount.json @@ -29,6 +29,20 @@ "resetPassword": "Reset Password", "resetPasswordConfirm": "Reset this account password to 123456? The user will be required to change it on next login.", "resetPasswordSuccess": "Password has been reset to 123456 and must be changed on next login", + "batchSelectedCount": "{count} selected", + "batchToolbarHint": "Batch actions are available for selected accounts", + "batchActionSelectRequired": "Please select at least one account", + "batchActionFailed": "Operation failed. Please try again later.", + "batchDelete": "Batch Delete", + "batchDeleteConfirm": "Delete the selected {count} accounts? Protected administrator accounts will be skipped and returned as failures.", + "batchDeleteSuccess": "Batch delete completed. {count} accounts were removed.", + "batchDeletePartialSuccess": "Batch delete completed. {successCount} succeeded and {errorCount} failed.", + "batchDeleteAllFailed": "Batch delete failed", + "batchResetPassword": "Batch Reset Password", + "batchResetPasswordConfirm": "Reset the selected {count} accounts to 123456? Users must change it on next login, and protected administrator accounts will be skipped.", + "batchResetPasswordSuccess": "{count} account passwords have been reset", + "batchResetPasswordPartialSuccess": "Batch password reset completed. {successCount} succeeded and {errorCount} failed.", + "batchResetPasswordAllFailed": "Batch password reset failed", "importTitle": "Import Users", "importUploadTitle": "Drag the Excel file here, or click to select a file", "importUploadDesc": "Only .xlsx / .xls files are supported. Import only creates users and duplicate accounts will fail.", diff --git a/easyflow-ui-admin/app/src/locales/langs/zh-CN/sysAccount.json b/easyflow-ui-admin/app/src/locales/langs/zh-CN/sysAccount.json index 8880375..1ed4a9d 100644 --- a/easyflow-ui-admin/app/src/locales/langs/zh-CN/sysAccount.json +++ b/easyflow-ui-admin/app/src/locales/langs/zh-CN/sysAccount.json @@ -30,6 +30,20 @@ "resetPassword": "重置密码", "resetPasswordConfirm": "确认将该用户密码重置为 123456 吗?重置后用户下次登录必须先修改密码。", "resetPasswordSuccess": "密码已重置为 123456,用户下次登录需修改密码", + "batchSelectedCount": "已选择 {count} 项", + "batchToolbarHint": "可对选中账号执行批量操作", + "batchActionSelectRequired": "请先选择要操作的账号", + "batchActionFailed": "操作失败,请稍后重试", + "batchDelete": "批量删除", + "batchDeleteConfirm": "确认批量删除已选中的 {count} 个账号吗?其中管理员账号将跳过并返回失败结果。", + "batchDeleteSuccess": "批量删除完成,共成功删除 {count} 个账号", + "batchDeletePartialSuccess": "批量删除完成,成功 {successCount} 个,失败 {errorCount} 个", + "batchDeleteAllFailed": "批量删除失败", + "batchResetPassword": "批量重置密码", + "batchResetPasswordConfirm": "确认将已选中的 {count} 个账号密码重置为 123456 吗?重置后用户下次登录必须先修改密码,管理员账号将跳过并返回失败结果。", + "batchResetPasswordSuccess": "已完成 {count} 个账号密码重置", + "batchResetPasswordPartialSuccess": "批量重置密码完成,成功 {successCount} 个,失败 {errorCount} 个", + "batchResetPasswordAllFailed": "批量重置密码失败", "importTitle": "导入用户", "importUploadTitle": "拖拽 Excel 文件到此处,或点击选择文件", "importUploadDesc": "仅支持 .xlsx / .xls,导入只新增用户,重复账号会报错", diff --git a/easyflow-ui-admin/app/src/views/system/sysAccount/SysAccountList.vue b/easyflow-ui-admin/app/src/views/system/sysAccount/SysAccountList.vue index ae472d3..c4fe0ab 100644 --- a/easyflow-ui-admin/app/src/views/system/sysAccount/SysAccountList.vue +++ b/easyflow-ui-admin/app/src/views/system/sysAccount/SysAccountList.vue @@ -1,7 +1,7 @@