用戶管理是任何系統的基礎功能之一,本篇介紹了如何實現一個完整的用戶管理模塊,包括用戶信息的增刪改查、用戶狀態管理、分頁查詢、數據驗證和權限控制。核心代碼實現部分涵蓋了控制器(UserController)、服務接口(IUserService)、請求/響應模型以及服務實現(UserServiceImpl),并詳細說明了各接口的功能,如獲取用戶信息、分頁獲取用戶列表、刪除用戶、禁用/啟用用戶和更新用戶信息。最佳實踐建議包括數據驗證、性能優化、安全性和代碼組織等方面,強調了使用數據注解、異步方法、依賴注入、權限控制和統一響應格式等措施。使用示例展示了如何通過HTTP請求獲取用戶列表、更新用戶信息和禁用用戶。注意事項提醒用戶刪除操作建議采用軟刪除、敏感操作需記錄日志、用戶狀態變更應通知相關系統、響應中不返回敏感信息以及并發操作的處理。該用戶管理模塊為基本的CRUD操作提供了實現基礎,并可根據實際需求擴展更多功能,如增加查詢條件、導入導出、操作日志等。
Tip:由于大部分內容跟單體應用中的知識點一樣,因此我們只講解核心設計要點和高級特性實現。
一、核心設計要點
1.1 用戶狀態管理
/// <summary>
/// 禁用/啟用用戶
/// </summary>
/// <param name="id"></param>
/// <param name="isDisabled"></param>
/// <returns></returns>
public async Task DisableUser(long id, bool isDisabled)
{var user = await _userManager.FindByIdAsync(id.ToString());if (user == null){throw new NotFoundException($"用戶不存在");}if (isDisabled){user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(100); // 設置一個很遠的未來時間}else{user.LockoutEnd = null; // 解除鎖定}user.LockoutEnabled = isDisabled;var result = await _userManager.UpdateAsync(user);if (!result.Succeeded){throw new BadRequestException($"禁用/啟用用戶失敗");}
}
這段代碼是一個用于“禁用/啟用用戶”的服務方法,常見于基于ASP.NET Core Identity的用戶管理系統。下面我們詳細講解代碼和設計思路。
代碼接收接收用戶ID和一個布爾值,表示是否禁用用戶。如果禁用,則將用戶的鎖定時間設置為100年后;如果啟用,則將鎖定時間設置為null
,表示解除鎖定。其中LockoutEnd
屬性用于控制用戶鎖定時間,LockoutEnabled
屬性用于啟用或禁用鎖定功能。代碼中使用了_userManager
來管理用戶對象,FindByIdAsync
方法用于查找用戶,UpdateAsync
方法用于更新用戶信息。在查找用戶時,如果找不到用戶,則拋出“用戶不存在”異常;在更新用戶信息時,如果更新失敗,則拋出“禁用/啟用用戶失敗”異常。
代碼的設計思路很簡單,我們采用ASP.NET Core Identity的鎖定機制,通過設置LockoutEnd
和LockoutEnabled
屬性來控制用戶的登錄權限,而不是直接刪除或修改用戶的敏感信息,從而提升系統的安全性。其次,方法中通過拋出異常的方式處理各種錯誤情況,便于上層統一捕獲和處理,提高了代碼的可維護性。此外,該實現方式具有良好的擴展性,只需調整鎖定時間或相關條件,即可滿足臨時禁用、永久禁用等不同業務場景的需求。
在實際開發過程中,需要注意以下幾點。第一,只有當 LockoutEnabled
屬性設置為 true
時,LockoutEnd
的鎖定機制才會生效。其次,將鎖定時間設置為100年后雖然不能算作真正的“永久禁用”,但對于大多數業務場景來說已經足夠使用。此外,如果用戶已經處于禁用狀態,再次執行禁用操作不會引發錯誤,但建議在業務邏輯層面實現冪等性處理,以避免重復操作帶來的潛在問題。
Tip:通過Identity的鎖定機制實現了用戶的禁用和啟用,既安全又易于維護,是.NET項目中常見的用戶狀態管理方式。
1.2 分頁查詢優化
/// <summary>
/// 獲取用戶列表
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
public async Task<PagedResponse<UserResponse>> GetUserList(UserPageRequest page)
{var query = _userManager.Users.AsQueryable();if (!string.IsNullOrEmpty(page.UserName)){query = query.Where(x => x.UserName.Contains(page.UserName));}if (!string.IsNullOrEmpty(page.Email)){query = query.Where(x => x.Email.Contains(page.Email));}// 使用單次查詢獲取總數和分頁數據var totalQuery = query;var pagedQuery = query.OrderByDescending(o => o.Id).Skip((page.Page - 1) * page.PageSize).Take(page.PageSize);// 并行執行兩個查詢var countTask = totalQuery.CountAsync();var usersTask = pagedQuery.ToListAsync();await Task.WhenAll(countTask, usersTask);int total = countTask.Result;List<SpUser> users = usersTask.Result;var result = new PagedResponse<UserResponse>{TotalRow = total,TotalPage = (int)Math.Ceiling((double)total / page.PageSize),Data = users.Select(x => new UserResponse{Id = x.Id,UserName = x.UserName,Email = x.Email,IsLocked = x.LockoutEnabled}).ToList()};return result;
}
這段代碼是一個用于獲取用戶列表的異步方法,常見于.NET后端服務中,主要實現了分頁、條件篩選和高效查詢。GetUserList
方法是一個異步操作,返回類型為 Task<PagedResponse<UserResponse>>
,用于獲取分頁后的用戶列表。參數 UserPageRequest page
封裝了分頁信息和篩選條件(如用戶名、郵箱等),便于靈活查詢用戶數據。這段代碼的設計思路是通過使用IQueryable
接口來構建動態查詢,結合分頁和異步操作來提高性能和用戶體驗。其中_userManager.Users
獲取了所有用戶的可查詢對象,AsQueryable()
方法使得可以對用戶數據進行動態查詢。并且我們使用了LINQ的 Where
方法來根據用戶名和郵箱進行條件篩選,OrderByDescending
方法按用戶ID倒序排列,Skip
和 Take
方法實現了分頁功能。這里要重點關注并行查詢總數和數據的實現,使用 Task.WhenAll
方法可以同時執行兩個異步查詢任務,分別獲取總記錄數和當前頁的用戶列表,從而提高查詢效率。
Tip:
Task.WhenAll
能提升性能,但要確保兩個查詢不會互相影響。
1.3 用戶信息更新
/// <summary>
/// 更新用戶信息
/// </summary>
/// <param name="id"></param>
/// <param name="user"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task UpdateUser(long id, UserUpdateRequest user)
{var spUser = await _userManager.FindByIdAsync(id.ToString());if (spUser == null){throw new NotFoundException($"用戶不存在");}spUser.UserName = user.UserName;spUser.Email = user.Email;var result = await _userManager.UpdateAsync(spUser);if (!result.Succeeded){throw new BadRequestException($"更新用戶失敗");}
}
這段代碼是一個用于更新用戶信息的服務方法,常見于基于ASP.NET Core Identity的用戶管理系統。代碼接收用戶ID和一個包含更新信息的請求對象 UserUpdateRequest
。首先通過 _userManager.FindByIdAsync
方法查找用戶,如果找不到用戶,則拋出“用戶不存在”異常。接著,將請求中的新信息賦值給用戶對象的相應屬性,如用戶名、郵箱和手機號。最后,使用 _userManager.UpdateAsync
方法更新用戶信息,如果更新失敗,則拋出“更新用戶信息失敗”異常,并將錯誤信息拼接成字符串返回。
代碼的設計思路很簡單,我們采用ASP.NET Core Identity的用戶管理功能,通過 _userManager
來操作用戶對象。首先通過ID查找用戶,確保用戶存在;然后更新用戶信息,確保數據的完整性和一致性。最后通過拋出異常的方式處理各種錯誤情況,便于上層統一捕獲和處理,提高了代碼的可維護性。此外,該實現方式具有良好的擴展性,只需調整請求對象中的屬性,即可滿足不同業務場景下的用戶信息更新需求。
二、高級特性實現
2.1 用戶角色關聯
/// <summary>
/// 獲取用戶信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<UserResponse?> GetUserInfo(long id)
{// 嘗試從緩存獲取string cacheKey = $"user:{id}";var cachedUser = await _redis.GetStringAsync(cacheKey);if (!string.IsNullOrEmpty(cachedUser)){return JsonSerializer.Deserialize<UserResponse>(cachedUser);}// 緩存未命中,從數據庫查詢var user = await _userManager.FindByIdAsync(id.ToString());if (user == null){throw new NotFoundException($"用戶不存在");}var response = new UserResponse{Id = user.Id,UserName = user.UserName,Email = user.Email,IsLocked = user.LockoutEnabled};// 緩存結果,設置適當的過期時間await _redis.SetStringAsync(cacheKey, JsonSerializer.Serialize(response), 60 * 10);return response;
}
這段代碼是一個用于獲取用戶信息的服務方法。代碼首先嘗試從Redis緩存中獲取用戶信息,如果緩存命中,則直接返回緩存中的數據;如果緩存未命中,則從數據庫中查詢用戶信息,并將查詢結果存入緩存中,以便下次快速訪問。代碼使用了 _userManager.FindByIdAsync
方法來查找用戶,如果找不到用戶,則拋出“用戶不存在”異常。查詢到的用戶信息被封裝成 UserResponse
對象,并存入Redis緩存中,設置了10分鐘的過期時間。
代碼的設計思路是通過使用Redis緩存來提高用戶信息查詢的性能,減少數據庫訪問次數。首先嘗試從緩存中獲取數據,如果緩存命中,則直接返回;如果緩存未命中,則從數據庫查詢并更新緩存。這樣可以顯著提高查詢速度,尤其是在高并發場景下。此外,使用Redis作為緩存存儲,可以有效降低數據庫負載,提高系統的整體性能。
Tip:使用Redis緩存用戶信息可以顯著提高查詢性能,尤其是在高并發場景下。
三、總結
用戶管理模塊是任何系統的基礎功能之一,本篇介紹了如何實現一個完整的用戶管理模塊,包括用戶信息的增刪改查、用戶狀態管理、分頁查詢、數據驗證和權限控制。核心代碼實現部分涵蓋了控制器(UserController)、服務接口(IUserService)、請求/響應模型以及服務實現(UserServiceImpl),并詳細說明了各接口的功能,如獲取用戶信息、分頁獲取用戶列表、刪除用戶、禁用/啟用用戶和更新用戶信息。