fix(user): 修复用户等级显示乱码,添加删除用户功能

- 修正用户等级常量:1=普通用户, 2=合伙人, 3=渠道合伙人
- 添加删除用户接口(硬删除),同时清除 user_refresh_tokens 和 user_login_logs
- 前端添加删除按钮和确认对话框
This commit is contained in:
zpc 2026-02-20 21:54:38 +08:00
parent 795e5586ac
commit 26902724c8
5 changed files with 142 additions and 17 deletions

View File

@ -149,5 +149,34 @@ public class UserController : BusinessControllerBase
}
}
/// <summary>
/// 删除用户(硬删除,同时清除登录记录和令牌)
/// </summary>
/// <param name="request">删除请求</param>
/// <returns>操作结果</returns>
[HttpPost("delete")]
[BusinessPermission("user:delete")]
public async Task<IActionResult> Delete([FromBody] DeleteRequest request)
{
if (!ModelState.IsValid)
{
return ValidationError("参数验证失败");
}
try
{
var result = await _userService.DeleteUserAsync(request.Id);
if (result)
{
return Ok("用户删除成功");
}
return Error(ErrorCodes.UserNotFound, "用户不存在");
}
catch (BusinessException ex)
{
return Error(ex.Code, ex.Message);
}
}
#endregion
}

View File

@ -52,12 +52,19 @@ public interface IUserBusinessService
Task<bool> UpdateUserLevelAsync(long id, int userLevel);
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD>б<EFBFBD>
/// 导出用户列表
/// </summary>
/// <param name="request"><EFBFBD><EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
/// <returns><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></returns>
/// <param name="request">查询参数</param>
/// <returns>导出数据</returns>
Task<List<UserDto>> ExportUsersAsync(UserQueryRequest request);
/// <summary>
/// 删除用户(硬删除,同时清除登录记录和令牌)
/// </summary>
/// <param name="id">用户ID</param>
/// <returns>是否成功</returns>
Task<bool> DeleteUserAsync(long id);
#endregion
#region ״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɽӿ<EFBFBD> - <EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD>

View File

@ -238,11 +238,53 @@ public class UserBusinessService : IUserBusinessService
})
.ToListAsync();
_logger.LogInformation("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD>б<EFBFBD><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: {Count}", items.Count);
_logger.LogInformation("导出用户列表成功,数量: {Count}", items.Count);
return items;
}
/// <inheritdoc />
public async Task<bool> DeleteUserAsync(long id)
{
var user = await _dbContext.Users
.Where(u => u.Id == id)
.FirstOrDefaultAsync();
if (user == null)
{
throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在");
}
// 使用事务确保数据一致性
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
// 删除用户刷新令牌
await _dbContext.Database.ExecuteSqlRawAsync(
"DELETE FROM user_refresh_tokens WHERE UserId = {0}", id);
// 删除用户登录日志
await _dbContext.Database.ExecuteSqlRawAsync(
"DELETE FROM user_login_logs WHERE UserId = {0}", id);
// 删除用户记录(硬删除)
_dbContext.Users.Remove(user);
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
_logger.LogInformation("删除用户成功ID: {UserId}, UID: {Uid}", id, user.Uid);
return true;
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "删除用户失败ID: {UserId}", id);
throw;
}
}
#endregion
#region <EFBFBD>ɽӿڣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD>

View File

@ -23,7 +23,7 @@ export interface UserItem {
nickname: string
/** 头像URL */
avatar: string
/** 用户等级 (0: 普通用户, 1: VIP, 2: 分销商) */
/** 用户等级 (1: 普通用户, 2: 合伙人, 3: 渠道合伙人) */
userLevel: number
/** 用户等级名称 */
userLevelName: string
@ -158,3 +158,16 @@ export function exportUsers(params: UserQuery): Promise<ApiResponse<Blob>> {
responseType: 'blob'
})
}
/**
*
* @param id ID
* @returns
*/
export function deleteUser(id: number): Promise<ApiResponse<boolean>> {
return request<boolean>({
url: '/admin/user/delete',
method: 'post',
data: { id }
})
}

View File

@ -157,7 +157,7 @@
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" width="150" fixed="right" align="center">
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleViewDetail(row)">
<el-icon><View /></el-icon>
@ -167,6 +167,10 @@
<el-icon><Edit /></el-icon>
等级
</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">
<el-icon><Delete /></el-icon>
删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -318,14 +322,15 @@
* @requirements 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7
*/
import { reactive, ref, onMounted } from 'vue'
import { Search, Refresh, View, Edit, Download, User } from '@element-plus/icons-vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { Search, Refresh, View, Edit, Download, User, Delete } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import {
getUserList,
getUserDetail,
updateUserStatus,
updateUserLevel,
exportUsers,
deleteUser,
type UserItem,
type UserDetail,
type UserQuery
@ -336,9 +341,9 @@ import { DictSelect } from '@/components'
/** 用户等级枚举 */
const USER_LEVEL = {
NORMAL: 0, //
VIP: 1, // VIP
DISTRIBUTOR: 2 //
NORMAL: 1, //
PARTNER: 2, //
CHANNEL: 3 //
} as const
/** 用户状态枚举 */
@ -455,9 +460,9 @@ function getLevelTagType(level: number): 'info' | 'success' | 'warning' | 'dange
switch (level) {
case USER_LEVEL.NORMAL:
return 'info'
case USER_LEVEL.VIP:
case USER_LEVEL.PARTNER:
return 'success'
case USER_LEVEL.DISTRIBUTOR:
case USER_LEVEL.CHANNEL:
return 'warning'
default:
return 'info'
@ -468,10 +473,10 @@ function getLevelName(level: number): string {
switch (level) {
case USER_LEVEL.NORMAL:
return '普通用户'
case USER_LEVEL.VIP:
return 'VIP'
case USER_LEVEL.DISTRIBUTOR:
return '分销商'
case USER_LEVEL.PARTNER:
return '合伙人'
case USER_LEVEL.CHANNEL:
return '渠道合伙人'
default:
return '未知'
}
@ -623,6 +628,35 @@ function handleChangeLevel(row: UserItem) {
state.levelDialogVisible = true
}
async function handleDelete(row: UserItem) {
try {
await ElMessageBox.confirm(
`确定要删除用户 "${row.nickname}"UID: ${row.uid})吗?删除后将同时清除该用户的登录记录和令牌数据,此操作不可恢复。`,
'删除确认',
{
confirmButtonText: '确定删除',
cancelButtonText: '取消',
type: 'warning'
}
)
} catch {
return
}
try {
const res = await deleteUser(row.id)
if (res.code === 0) {
ElMessage.success('用户删除成功')
await loadUserList()
} else {
throw new Error(res.message || '删除失败')
}
} catch (error) {
const message = error instanceof Error ? error.message : '删除失败'
ElMessage.error(message)
}
}
async function handleLevelSubmit() {
if (!levelFormRef.value) return