NetCore3.1及Vue開發通用RBAC前後端通用框架
框架說明
該框架是本人學習過程中本着只有自己動手操作一遍才能真正理解,和遇到對應問題並解決問題的思路。和為了能在開發相應系統時能快速搭建出相關框架而做出的基於NetCore3.1+Vue的RBAC通用權限框架。
只有在敲的過程中才能遇見細節上的問題,成長無非就是發現問題、思考問題、解決問題、總結沉澱,後面才能去規避和提高代碼質量。
如有發現什麼錯誤,請聯繫我,將第一時間改正。互相學習,共同進步。
不要只光看,最終要的是要自己敲,不自己敲的話,當遇到了還是不會、一臉懵的!
前端測試地址 lion.levy.net.cn , 可以自己註冊,也可使用已有賬號密碼登錄。請不要用已有賬號修改或刪除系統已有數據,謝謝配合
前端代碼地址 Gitee GitHub
後端接口地址 lion.levy.net.cn
後端代碼地址 Gitee GitHub
** 如果您覺得對您有幫助的話,可以給個start,謝謝 **
項目框架圖
EntityFrameworkCore CodeFirist開發 支持 MySql/SqlServer
數據遷移時自動生成種子數據,Quartz 表結構還請自行通過docs文件中對應數據庫類型文件,執行添加表語句
異步 async/await 開發
採用類似倉儲模式開發,封裝底層常用數據庫方法
Nlog日誌記錄,可記錄請求日誌/異常日誌等,調試階段打印SQL語句
支持AOP切面編程,如日誌、緩存、事務處理等
權限管理系統支持多租戶多權限管理,頁面動態管理頁面&按鈕權限
採用自定義JWT身份認證,可自帶參數,驗證過期時間,滑動無感重新頒發Token
支持Redis 和 Memory 兩種緩存
使用三方DI AutoFac,實行批量註冊,支持屬性注入
採用Automapper做對象映射,並支持擴展方法直接映射。
使用 Quartz.net做任務調度,支持集群調度(未測試該情況,代碼中有注釋說明),支持反射調用本地程序集方式,支持調用API方式
使用Swagger提供API文檔,並實現接口文檔中填寫授權信息方便調試。
支持CORS跨域,當前使用的是全跨域模式。
支持監控心跳檢查HealthChecks,可實現對其它網頁或服務的檢查和通知。(代碼沒寫,如需要,可聯繫我獲取。通知支持釘釘和企業微信等)
支持RabbitMq消息隊列,實現了死信隊列和延遲隊列。
支持docker部署,支持docker中部署同一網段
可配合Jenkins加 Sonar 做CI/CD & 代碼質量檢查
Nginx可配置實現負載均衡
可配置 IpRateLimiting 做API限流處理(此前配置試過,沒有起到效果,可能哪個地方衝突了),若需要,也可根據redis實現一個簡易版的
多租戶權限設計表
詳細說明
Deleted、CreatedTime、CreatedBy、UpdatedTime、UpdatedBy 為常用表默認字段,可自行擴展增加Remark、Status、Sort等為表通用字段。特別說明,若數據量很大的表,可將非關鍵字段拆分出來,減小表大小,提高查詢速度。
- 數據都是按頁存在,頁存儲空間有限,將非關鍵信息存另外的一個表,關鍵信息表所存數據頁就少,查詢速度就相應的提高了。 個人理解
Sys_Tenant租戶表
- 租戶相當於是將系統租給某個公司使用,租戶表管理公司企業信息,這裡表結構只是給了一個示例。
- 若有什麼想要控制,就設計成相應的表,若數據資源固定則設計為字典表。
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
TenantId | 主鍵 | bigint | TRUE |
TenantName | 租戶名稱 | nvarchar(50) | |
Remark | 備註 | nvarchar(32) | |
State | 狀態:1-啟用;-1-禁用 | int | |
CreatedTime | 創建時間 | datetime |
sys_user用戶表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
UserId | 主鍵 | bigint | TRUE |
TenantId | 租戶ID | bigint | |
NickName | 用戶名 | nvarchar(30) | |
PassWord | 密碼 | nvarchar(512) | |
nvarchar(128) | |||
Sex | 性別:0-女;1-男 | int | |
State | 狀態:1-啟用;-1-禁用 | int | |
CreatedTime | 創建時間 | datetime | |
CreatedBy | 創建人 | bigint | |
UpdatedTime | 修改時間 | datetime | |
UpdatedBy | 修改人 | bigint |
Sys_Role角色表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
RoleId | 主鍵 | bigint | TRUE |
TenantId | 租戶ID | bigint | |
RoleName | 角色名稱 | nvarchar(25) | |
RoleDesc | 角色描述 | nvarchar(128) | |
Deleted | 邏輯刪除標誌 | tinyint | |
CreatedTime | 創建時間 | datetime | |
CreatedBy | 創建人 | bigint | |
UpdatedTime | 修改時間 | datetime | |
UpdatedBy | 修改人 | bigint |
Sys_User_Role_Relation用戶角色關係表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
UserId | 用戶ID | bigint | TRUE |
RoleId | 角色Id | bigint | TRUE |
TenantId | 租戶ID | bigint | TRUE |
State | 狀態:1-啟用;-1-禁用 | int | |
Deleted | 邏輯刪除標誌 | tinyint | |
CreatedTime | 創建時間 | datetime | |
CreatedBy | 創建人 | bigint | |
UpdatedTime | 修改時間 | datetime | |
UpdatedBy | 修改人 | bigint |
Sys_Menu權限資源表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
MenuId | 主鍵,自定義 | nvarchar(40) | TRUE |
MenuName | 資源名稱 | nvarchar(64) | |
ParentMenuId | 父級Id–無限級菜單(長度加大) | nvarchar(40) | |
Level | 菜單層級 | int | |
Url | 資源地址 | nvarchar(256) | |
Type | 資源類型:1:菜單 2:按鈕 | int | |
Icon | 圖標 | nvarchar(128) | |
OrderIndex | 順序 | int | |
Deleted | 邏輯刪除標誌 | tinyint | |
CreatedTime | 創建時間 | datetime | |
CreatedBy | 創建人 | bigint | |
UpdatedTime | 修改時間 | datetime | |
UpdatedBy | 修改人 | bigint |
Sys_Role_Menu_Relation角色資源關係表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
MenuId | 資源ID | nvarchar(40) | TRUE |
RoleId | 角色Id | bigint | TRUE |
TenantId | 租戶ID | bigint | TRUE |
State | 狀態:1-啟用;-1-禁用 | int | |
Deleted | 邏輯刪除標誌 | tinyint | |
CreatedTime | 創建時間 | datetime | |
CreatedBy | 創建人 | bigint | |
UpdatedTime | 修改時間 | datetime | |
UpdatedBy | 修改人 | bigint |
- 調度相關-額外創建的管理表
Sys_Quartz調度任務綜合表
字段名 | 說明 | 類型 | 主鍵 |
---|---|---|---|
JobGroup | 任務分組 | nvarchar(200) | TRUE |
JobName | 任務名稱 | nvarchar(200) | TRUE |
JobType | 任務類型:0-無,1-api,2-程序集 | int | |
BeginTime | 開始時間 | datetime | |
EndTime | 結束時間 | datetime | |
Cron | Cron表達式 | nvarchar(40) | |
RunTimes | 執行次數 | int | |
IntervalSecond | 循環次數 | int | |
TriggerType | 任務類型:0-無,1-cron,2-簡單類型 | int | |
RequestPath | API地址或請求類程序集名稱 | nvarchar(56) | |
RequestMethod | API請求類型或請求類地址 | nvarchar(40) | |
RequestParameters | 請求Body參數 | nvarchar(512) | |
Headers | 請求頭參數 | nvarchar(256) | |
Priority | 執行優先級,等級越高,相同時間先執行 | int | |
Description | 任務描述 | nvarchar(256) | |
NotifyEmail | 通知郵箱 | nvarchar(128) | |
MailMessage | 郵件通知類型:0-不通知,1-錯誤通知,2-全量通知 | int | |
TriggerState | 暫停 錯誤 阻塞 完成 等 | int | |
PreviousFireTime | 上次執行時間 | datetime | |
NextFireTime | 下次執行時間 | datetime | |
CreatedTime | 創建時間 | datetime |
效果圖
當用戶註冊使用,便是一個租戶,租戶也就代表是一個公司群體,可以以該賬號創建子賬號,給子賬號分配頁面及按鈕權限。
接口文檔
用戶界面
角色管理界面
系統管理菜單界面
調度任務界面
調度-接口及cron界面
調度-程序集反射方式創建界面
調度日誌界面
後端拉取運行
-
需配置
appsettings.json
中的相關信息 配置郵箱發送人信息,本項目配置的是QQ郵箱,請根據需要配置,用以註冊和找回密碼的短訊發送 -
需配置
appsettings.json
中的相關信息 配置數據庫鏈接信息,執行數據庫遷移命令(查看第7點),執行完成後,根據doc文件中選擇Quartz
對應數據庫腳本語言創建相關表。 -
配置NLog.config文件數據庫相關信息,用於記錄日誌,根據使用數據庫選擇相應鏈接,並配置數據庫鏈接
-
需配置
appsettings.json
中的相關信息Redis
鏈接信息 -
需配置
appsettings.json
中的相關信息RabbitMQ
鏈接信息 -
需配置
appsettings.json
中的相關信息Redis
鏈接信息 -
在
LionFrame.Data
項目中有個種子數據文件夾,在數據遷移時會添加相關信息
前端項目請參考 前端系列
在博客中之前已經寫過前端系列,可參考下,有不清楚的地方可在個人簡介中找到我的聯繫方式。若有什麼錯誤的地方也歡迎指正~
發佈到docker中
可參考項目中的docs文件夾下的 同一網絡部署到Docker中 文件。因為我這邊redis和mysql已經在之前就已經創建過了,所以此次只做了 MQ和項目發佈到同一網段中,其它的可如法炮製。喜歡玩的也可以使用docker-compose
來進行編寫第一次的發佈腳本,因為多環境的問題,建議使用docker-compose
,可快速的進行部署,也可避免命令敲錯等情況。
只有初次發佈時配置可能比較麻煩,後面基本就只有項目需要多次發佈,可引入jenkins 做 CI/CD,基本支持中小企業使用
若項目做的比較大可升級K8S,做彈性伸縮,nginx做負載均衡等等、
前端項目這裡由於是騰訊雲的學生服務器,比較卡頓,故申請了一個騰訊雲的免費6個月的存儲桶COS進行發佈前端項目。
netcore3.1 發佈到docker中所遇到的坑及解決
由於docker中沒有圖片的依賴組件,在我們生成二維碼的時候的時候需要使用到System.Drawing.Common
來使用 Image、Bitmap 等類型,通過docker logs 可查看到如下異常
System.TypeInitializationException: The type initializer for 'Gdip' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'libgdiplus': The specified module could not be found.
解決方法,在dockerfile
中加上如下語句RUN apt-get update && apt-get install -y libgdiplus
由於國內網絡原因,此處可能需要下載數十分鐘,為了提高數據,可在dockerfile
中加上RUN cp sources.list /etc/apt/
一句話,使用鏡像源,來提高下載速度,sources.list
文件在docs文件夾中有提供。
這樣就能解決圖片的問題
ps:小插曲,其實最開始我在這樣處理後還是不能生成圖片、日誌中也看不到錯誤,只知道容器在調用二維碼生成接口時就會退出,起先還以為是dockerfile引入有問題,
在各大網站查找相關訊息,博客園/csdn/stackoverflow/github等網站上查找。
在這個網址上 //github.com/dotnet/dotnet-docker/issues/618 看到人家都是這樣解決的,為啥我就不行
不禁陷入了沉思,突然想到可能是代碼的問題,換了生成驗證碼的方式, 驚奇的發現可以了。有的時候得換一種思考的方式。