@elf-express/admin-net-mcp 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/index.js +83 -88
  2. package/knowledge/Furion_Teaching_Manual/04-1-/351/205/215/347/275/256.md +442 -0
  3. package/knowledge/Furion_Teaching_Manual/04-2-/351/201/270/351/240/205.md +363 -0
  4. package/knowledge/Furion_Teaching_Manual/05-1-/345/213/225/346/205/213WebAPI.md +825 -0
  5. package/knowledge/Furion_Teaching_Manual/05-2-HttpContext.md +217 -0
  6. package/knowledge/Furion_Teaching_Manual/05-3-/347/257/251/351/201/270/345/231/250/346/224/224/346/210/252/345/231/250AOP.md +581 -0
  7. package/knowledge/Furion_Teaching_Manual/05-4-/350/253/213/346/261/202/347/250/275/346/240/270/346/227/245/350/252/214.md +129 -0
  8. package/knowledge/Furion_Teaching_Manual/05-5-/344/270/255/344/273/213/350/273/237/351/253/224Middleware.md +328 -0
  9. package/knowledge/Furion_Teaching_Manual/05-6-Vue-React-Angular/344/273/213/351/235/242/344/273/243/347/220/206.md +317 -0
  10. package/knowledge/Furion_Teaching_Manual/06-1/350/246/217/347/257/204/345/214/226/346/216/245/345/217/243.md +1458 -0
  11. package/knowledge/Furion_Teaching_Manual/06-2/347/254/254/344/270/211/346/226/271API_Scalar.md +91 -0
  12. package/knowledge/Furion_Teaching_Manual/07-/345/217/213/345/245/275/344/276/213/345/244/226/350/231/225/347/220/206.md +511 -0
  13. package/knowledge/Furion_Teaching_Manual/08-1-/350/263/207/346/226/231/351/251/227/350/255/211/345/237/272/347/244/216/344/275/277/347/224/250.md +587 -0
  14. package/knowledge/Furion_Teaching_Manual/10-1-SqlSugar/346/225/264/345/220/210.md +336 -0
  15. package/knowledge/Furion_Teaching_Manual/11-SaaS /345/244/232/347/247/237/346/210/266/347/255/206/350/250/230.md" +271 -0
  16. package/knowledge/Furion_Teaching_Manual/12-furion-dependency-injection.md +408 -0
  17. package/knowledge/Furion_Teaching_Manual/13-/347/211/251/344/273/266/350/263/207/346/226/231/346/230/240/345/260/204/357/274/210Mapster/357/274/211.md +162 -0
  18. package/knowledge/Furion_Teaching_Manual/14-/345/210/206/345/270/203/345/274/217/347/274/223/345/255/230.md +311 -0
  19. package/knowledge/Furion_Teaching_Manual/15-/345/256/211/345/205/250/351/211/264/346/235/203.md +832 -0
  20. package/knowledge/Furion_Teaching_Manual/17-/346/252/242/350/246/226/347/257/204/346/234/254/345/274/225/346/223/216.md +327 -0
  21. package/knowledge/Furion_Teaching_Manual/18-/346/227/245/350/252/214/350/250/230/351/214/204.md +639 -0
  22. package/knowledge/Furion_Teaching_Manual/19-1-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/237/272/347/244/216/344/275/277/347/224/250.md +621 -0
  23. package/knowledge/Furion_Teaching_Manual/19-2-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/351/200/262/351/232/216/346/214/207/345/215/227.md +928 -0
  24. package/knowledge/Furion_Teaching_Manual/19-3-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/270/270/350/246/213/345/225/217/351/241/214.md +362 -0
  25. package/knowledge/Furion_Teaching_Manual/20-/350/263/207/346/226/231/345/212/240/350/247/243/345/257/206.md +286 -0
  26. package/knowledge/Furion_Teaching_Manual/20-/351/231/204/351/214/204-KSort/350/263/207/346/226/231/347/260/275/345/220/215/345/256/214/346/225/264/345/216/237/345/247/213/347/242/274.md +305 -0
  27. package/knowledge/Furion_Teaching_Manual/21-/345/205/250/347/220/203/345/214/226/345/222/214/346/234/254/345/234/260/345/214/226.md +342 -0
  28. package/knowledge/Furion_Teaching_Manual/22-/344/272/213/344/273/266/345/214/257/346/265/201/346/216/222EventBus.md +448 -0
  29. package/knowledge/Furion_Teaching_Manual/23-JSON/345/272/217/345/210/227/345/214/226.md +340 -0
  30. package/knowledge/Furion_Teaching_Manual/24-/345/215/263/346/231/202/351/200/232/350/250/212SignalR.md +247 -0
  31. package/knowledge/Furion_Teaching_Manual/25-/350/274/224/345/212/251/350/247/222/350/211/262/346/234/215/345/213/231WorkerService.md +295 -0
  32. package/knowledge/Furion_Teaching_Manual/26-1-/346/216/222/347/250/213/344/275/234/346/245/255/345/256/232/346/231/202/344/273/273/345/213/231.md +631 -0
  33. package/knowledge/Furion_Teaching_Manual/26-2-Cron/350/241/250/351/201/224/345/274/217.md +203 -0
  34. package/knowledge/Furion_Teaching_Manual/26-3-/344/273/273/345/213/231/344/275/207/345/210/227TaskQueue.md +215 -0
  35. package/knowledge/Furion_Teaching_Manual/27-/345/210/206/346/225/243/345/274/217ID/347/224/237/346/210/220.md +86 -0
  36. package/knowledge/Furion_Teaching_Manual/28-/346/250/241/347/265/204/345/214/226/351/226/213/347/231/274.md +68 -0
  37. package/knowledge/Furion_Teaching_Manual/29-/346/265/201/350/256/212/347/211/251/344/273/266Clay.md +313 -0
  38. package/knowledge/Furion_Teaching_Manual/30-/350/204/253/346/225/217/350/231/225/347/220/206/357/274/210Sensitive Detection).md" +168 -0
  39. package/knowledge/Furion_Teaching_Manual/32-/346/234/203/350/251/261/345/222/214/347/213/200/346/205/213/347/256/241/347/220/206.md +147 -0
  40. package/knowledge/Furion_Teaching_Manual/33-IPC/347/250/213/345/272/217/351/200/232/350/250/212.md +98 -0
  41. package/knowledge/Furion_Teaching_Manual/34-2-Docker/351/203/250/347/275/262.md +313 -0
  42. package/knowledge/Furion_Teaching_Manual/34-3-Nginx/351/203/250/347/275/262.md +157 -0
  43. package/knowledge/Furion_Teaching_Manual/34-8-WindowsService/351/203/250/347/275/262.md +112 -0
  44. package/knowledge/Furion_Teaching_Manual/35-1-Docker/347/222/260/345/242/203/346/214/201/347/272/214/351/203/250/347/275/262Jenkins.md +169 -0
  45. package/knowledge/Furion_Teaching_Manual/36-1-/345/226/256/345/205/203/346/270/254/350/251/246/346/225/264/345/220/210/346/270/254/350/251/246.md +275 -0
  46. package/knowledge/Furion_Teaching_Manual/36-3-/345/237/272/346/272/226/346/270/254/350/251/246Benchmarking.md +80 -0
  47. package/knowledge/attributes.md +153 -0
  48. package/knowledge/config.md +147 -0
  49. package/knowledge/entity.md +115 -0
  50. package/knowledge/event.md +124 -0
  51. package/knowledge/plugin.md +136 -0
  52. package/knowledge/service.md +146 -0
  53. package/knowledge/sqlsugar.md +172 -0
  54. package/knowledge/vue-typescript.md +338 -0
  55. package/package.json +3 -2
@@ -0,0 +1,147 @@
1
+ # Admin.NET 設定系統指南
2
+
3
+ ## IConfigurableOptions 模式
4
+
5
+ Furion 自動將 JSON 設定檔綁定到實作 IConfigurableOptions 的類別,無需手動 Configure<T>()。
6
+
7
+ ### 基本模式
8
+ ```csharp
9
+ // 設定類別(放在 Admin.NET.Core/Option/ 或 Admin.NET.Application/Option/)
10
+ public sealed class SMSOptions : IConfigurableOptions
11
+ {
12
+ public int VerifyCodeExpireSeconds { get; set; } = 60; // 預設值
13
+ public string Provider { get; set; }
14
+ public AliyunSettings Aliyun { get; set; }
15
+ }
16
+
17
+ // JSON 檔案名稱需與類別名稱去掉 Options 後綴對應
18
+ // SMSOptions → SMS.json(位於 Admin.NET.Application/Configuration/SMS.json)
19
+ ```
20
+
21
+ ### 需要後處理的模式(PostConfigure)
22
+ ```csharp
23
+ public sealed class DbConnectionOptions : IConfigurableOptions<DbConnectionOptions>
24
+ {
25
+ public List<DbConnectionConfig> ConnectionConfigs { get; set; }
26
+
27
+ public void PostConfigure(DbConnectionOptions options, IConfiguration configuration)
28
+ {
29
+ // JSON 載入後的自訂處理
30
+ foreach (var config in options.ConnectionConfigs)
31
+ {
32
+ if (config.ConfigId == null)
33
+ config.ConfigId = SqlSugarConst.MainConfigId;
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### 在 Service 中使用
40
+ ```csharp
41
+ public class SmsService : IDynamicApiController, ITransient
42
+ {
43
+ private readonly SMSOptions _smsOptions;
44
+
45
+ public SmsService(IOptions<SMSOptions> smsOptions)
46
+ {
47
+ _smsOptions = smsOptions.Value;
48
+ }
49
+ }
50
+ ```
51
+
52
+ ## 設定檔位置
53
+
54
+ 所有 JSON 設定檔放在 `Admin.NET.Application/Configuration/`:
55
+
56
+ | 檔案 | 說明 | 對應 Options 類別 |
57
+ |------|------|------------------|
58
+ | Database.json | 資料庫連線、DB 類型 | DbConnectionOptions |
59
+ | Cache.json | Redis 或記憶體快取 | CacheOptions |
60
+ | App.json | Swagger、插件、CORS、虛擬路徑 | AppOptions |
61
+ | JWT.json | Token 設定(有效期、金鑰) | JWTSettingsOptions |
62
+ | SMS.json | 簡訊供應商設定 | SMSOptions |
63
+ | Alipay.json | 支付寶設定 | AlipayOptions |
64
+ | Wechat.json | 微信設定 | WechatOptions |
65
+ | Logging.json | Serilog 設定 | — |
66
+ | EventBus.json | 事件匯流排(記憶體/Redis/RabbitMQ) | — |
67
+
68
+ ## Database.json 結構
69
+
70
+ ```json
71
+ {
72
+ "DbConnection": {
73
+ "EnableConsoleSql": true,
74
+ "SuperAdminIgnoreIDeletedFilter": false,
75
+ "ConnectionConfigs": [
76
+ {
77
+ "ConfigId": "0",
78
+ "DbNickName": "主資料庫",
79
+ "DbType": 0,
80
+ "ConnectionString": "Server=.;Database=AdminNET;...",
81
+ "DbSettings": {
82
+ "EnableInitDb": true,
83
+ "EnableDiffLog": false,
84
+ "EnableUnderLine": false
85
+ },
86
+ "TableSettings": {
87
+ "EnableInitTable": true,
88
+ "EnableIncreTable": true
89
+ },
90
+ "SeedSettings": {
91
+ "EnableInitSeed": true,
92
+ "EnableIncreSeed": false
93
+ }
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## DbType 對應
101
+
102
+ | 值 | 資料庫 |
103
+ |----|--------|
104
+ | 0 | MySql |
105
+ | 1 | SqlServer |
106
+ | 2 | Sqlite |
107
+ | 3 | Oracle |
108
+ | 4 | PostgreSQL |
109
+ | 5 | Dm(達夢) |
110
+ | 6 | Kdbndp(人大金倉) |
111
+
112
+ ## TenantType 多租戶策略
113
+
114
+ | 值 | 策略 | 說明 |
115
+ |----|------|------|
116
+ | Id | 欄位隔離 | 同一資料庫,用 TenantId 欄位區分 |
117
+ | Schema | Schema 隔離 | 同一資料庫,不同 Schema |
118
+ | Database | 資料庫隔離 | 完全獨立的資料庫 |
119
+
120
+ ## App.json 關鍵設定
121
+
122
+ ```json
123
+ {
124
+ "App": {
125
+ "ExternalAssemblies": ["plugins"], // 插件目錄
126
+ "CorsUrls": ["http://localhost:9100"], // 允許的 CORS 來源
127
+ "VirtualFileServerOptions": [...], // 虛擬路徑對應
128
+ "UnifyResultSettings": { // 統一回應格式設定
129
+ "DefaultSuccessCode": 200
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ ## 讀取任意設定(不透過 Options 類別)
136
+
137
+ ```csharp
138
+ // 直接讀取設定值
139
+ var value = App.GetConfig<string>("App:CorsUrls:0");
140
+
141
+ // 取得設定區段
142
+ var section = App.Configuration.GetSection("Database:ConnectionConfigs");
143
+ ```
144
+
145
+ ## 檔案位置
146
+ - Options 類別:`Admin.NET.Core/Option/`
147
+ - JSON 設定檔:`Admin.NET.Application/Configuration/`
@@ -0,0 +1,115 @@
1
+ # Admin.NET 實體(Entity)設計指南
2
+
3
+ ## 基礎類別階層
4
+
5
+ 所有實體都使用 Snowflake ID 作為主鍵(long 型別,由 YitIdHelper.NextId() 自動產生)。
6
+
7
+ ```
8
+ EntityBaseId — 只有 Id(long)
9
+ └─ EntityBase — + 審計欄位(CreateTime, UpdateTime, 建立/更新者)
10
+ ├─ EntityBaseDel — + 軟刪除(IsDelete, DeleteTime)
11
+ ├─ EntityBaseOrg — + 組織(OrgId)
12
+ ├─ EntityBaseTenant — + 租戶(TenantId)
13
+ ├─ EntityBaseTenantId — TenantId 別名變體
14
+ ├─ EntityBaseOrgDel — Org + 軟刪除
15
+ ├─ EntityBaseTenantDel — Tenant + 軟刪除
16
+ ├─ EntityBaseTenantOrg — Tenant + Org
17
+ └─ EntityBaseTenantOrgDel — 所有功能(最完整)
18
+ ```
19
+
20
+ ## 各基礎類別屬性
21
+
22
+ ### EntityBase(所有實體共有)
23
+ | 屬性 | 型別 | 說明 |
24
+ |------|------|------|
25
+ | Id | long | Snowflake ID,AOP 自動生成 |
26
+ | CreateTime | DateTime | 建立時間,AOP Insert 時填入 DateTime.Now |
27
+ | UpdateTime | DateTime? | 更新時間,AOP Update 時填入 DateTime.Now |
28
+ | CreateUserId | long? | 建立者 ID,從 ClaimConst.UserId 取得 |
29
+ | CreateUserName | string | 建立者姓名,從 ClaimConst.RealName 取得 |
30
+ | UpdateUserId | long? | 更新者 ID,AOP 自動填入 |
31
+ | UpdateUserName | string | 更新者姓名,AOP 自動填入 |
32
+
33
+ ### EntityBaseDel(軟刪除)
34
+ | 屬性 | 型別 | 說明 |
35
+ |------|------|------|
36
+ | IsDelete | bool | 是否刪除,預設 false |
37
+ | DeleteTime | DateTime? | 刪除時間,IsDelete=true 時由 AOP 填入 |
38
+
39
+ ### EntityBaseOrg
40
+ | 屬性 | 型別 | 說明 |
41
+ |------|------|------|
42
+ | OrgId | long | 組織 ID,用於資料權限範圍控管 |
43
+
44
+ ### EntityBaseTenant
45
+ | 屬性 | 型別 | 說明 |
46
+ |------|------|------|
47
+ | TenantId | long? | 租戶 ID,AOP Insert 時從 ClaimConst.TenantId 填入 |
48
+
49
+ ## 如何選擇基礎類別
50
+
51
+ - **一般業務資料**(有審計需求):→ `EntityBase`
52
+ - **可軟刪除的業務資料**:→ `EntityBaseDel`
53
+ - **需要組織隔離**(資料權限):→ `EntityBaseOrgDel` 或 `EntityBaseOrg`
54
+ - **多租戶資料**:→ `EntityBaseTenantDel` 或 `EntityBaseTenant`
55
+ - **完整功能(多租戶 + 組織 + 軟刪除)**:→ `EntityBaseTenantOrgDel`
56
+ - **系統配置/日誌(不需審計)**:→ `EntityBaseId`
57
+
58
+ ## 自動過濾介面
59
+
60
+ 實體實作以下介面後,SqlSugar AOP 會自動套用 WHERE 條件:
61
+
62
+ ```csharp
63
+ public interface IDeletedFilter { bool IsDelete { get; set; } } // 自動加 WHERE IsDelete=0
64
+ public interface ITenantIdFilter { long? TenantId { get; set; } } // 自動加 WHERE TenantId=目前租戶
65
+ public interface IOrgIdFilter { long OrgId { get; set; } } // 自動加組織資料權限過濾
66
+ ```
67
+
68
+ **注意**:EntityBaseDel 已實作 IDeletedFilter,EntityBaseTenant 已實作 ITenantIdFilter,無需手動實作。
69
+
70
+ ## 實體路由(資料庫選擇)
71
+
72
+ SqlSugar Repository 會依照以下 Attribute 決定連接哪個資料庫:
73
+
74
+ | Attribute | 用途 |
75
+ |-----------|------|
76
+ | `[SysTableAttribute]` | 強制使用主資料庫連線 |
77
+ | `[TenantAttribute]` | 路由至租戶專屬資料庫 |
78
+ | `[LogTableAttribute]` | 路由至獨立 Log 資料庫 |
79
+ | `[IgnoreTableAttribute]` | 不建立資料表 |
80
+
81
+ ## 初始化相關 Attribute
82
+
83
+ | Attribute | 說明 |
84
+ |-----------|------|
85
+ | `[SeedDataAttribute]` | 標記此實體有種子資料 |
86
+ | `[IncreTableAttribute]` | 允許增量更新資料表(新增欄位) |
87
+ | `[IncreSeedAttribute]` | 允許增量更新種子資料 |
88
+ | `[IgnoreUpdateSeedAttribute]` | 跳過種子資料更新 |
89
+
90
+ ## 範例
91
+
92
+ ```csharp
93
+ // 一般業務資料(有軟刪除)
94
+ [SugarTable("sys_order", "訂單表")]
95
+ public class SysOrder : EntityBaseDel
96
+ {
97
+ [SugarColumn(ColumnDescription = "訂單編號", Length = 32)]
98
+ public string OrderNo { get; set; }
99
+
100
+ [SugarColumn(ColumnDescription = "金額", ColumnDataType = "decimal(18,2)")]
101
+ public decimal Amount { get; set; }
102
+ }
103
+
104
+ // 多租戶資料(含組織 + 軟刪除)
105
+ [SugarTable("biz_project", "專案表")]
106
+ public class BizProject : EntityBaseTenantOrgDel
107
+ {
108
+ [SugarColumn(ColumnDescription = "專案名稱", Length = 100)]
109
+ public string ProjectName { get; set; }
110
+ }
111
+ ```
112
+
113
+ ## 檔案位置
114
+ - 基礎類別:`Admin.NET.Core/Entity/EntityBase.cs`
115
+ - 過濾介面:`Admin.NET.Core/Entity/IEntityFilter.cs`
@@ -0,0 +1,124 @@
1
+ # Admin.NET 事件匯流排指南
2
+
3
+ ## 基本概念
4
+
5
+ Admin.NET 使用 Furion 的事件匯流排,透過 [EventSubscribe] 屬性訂閱事件,解耦業務邏輯。
6
+
7
+ ## 訂閱者模式
8
+
9
+ ```csharp
10
+ public class OrderEventSubscriber : IEventSubscriber, ISingleton, IDisposable
11
+ {
12
+ private readonly IServiceScopeFactory _scopeFactory;
13
+
14
+ public OrderEventSubscriber(IServiceScopeFactory scopeFactory)
15
+ {
16
+ _scopeFactory = scopeFactory;
17
+ }
18
+
19
+ [EventSubscribe("Order:Created")]
20
+ public async Task OnOrderCreated(EventHandlerExecutingContext context)
21
+ {
22
+ // context.Source.EventId — 事件名稱
23
+ // context.Source.Payload — 事件資料(object)
24
+ var order = context.Source.Payload as Order;
25
+
26
+ using var scope = _scopeFactory.CreateScope();
27
+ var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
28
+ await emailService.SendOrderConfirmation(order);
29
+ }
30
+
31
+ [EventSubscribe("Order:Cancelled")]
32
+ public async Task OnOrderCancelled(EventHandlerExecutingContext context)
33
+ {
34
+ var payload = (OrderCancelledPayload)context.Source.Payload;
35
+ // 處理取消邏輯
36
+ }
37
+
38
+ public void Dispose() { }
39
+ }
40
+ ```
41
+
42
+ ## 必要條件
43
+
44
+ | 項目 | 說明 |
45
+ |------|------|
46
+ | `IEventSubscriber` | 必須實作此介面 |
47
+ | `ISingleton` | 通常使用 Singleton(也可 ITransient) |
48
+ | `IDisposable` | 建議實作,釋放資源 |
49
+ | `IServiceScopeFactory` | 訂閱者是 Singleton,需透過 Scope 使用 Scoped 服務 |
50
+
51
+ ## 發布事件
52
+
53
+ ### 方式一:非同步發布(推薦)
54
+ ```csharp
55
+ await App.GetRequiredService<IEventPublisher>().PublishAsync(
56
+ new ChannelEventSource("Order:Created", order));
57
+ ```
58
+
59
+ ### 方式二:在 AOP / Filter 中發布
60
+ ```csharp
61
+ await App.PublishAsync(new EventSource
62
+ {
63
+ EventId = CommonConst.AddExLog,
64
+ Payload = logEntity
65
+ });
66
+ ```
67
+
68
+ ### 方式三:透過注入的 IEventPublisher
69
+ ```csharp
70
+ public class OrderService : IDynamicApiController, ITransient
71
+ {
72
+ private readonly IEventPublisher _eventPublisher;
73
+
74
+ public OrderService(IEventPublisher eventPublisher)
75
+ {
76
+ _eventPublisher = eventPublisher;
77
+ }
78
+
79
+ public async Task CreateOrder(CreateOrderInput input)
80
+ {
81
+ var order = await SaveOrder(input);
82
+ await _eventPublisher.PublishAsync("Order:Created", order);
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## 系統內建事件
88
+
89
+ ### CommonConst 定義的事件
90
+ | 事件名稱 | 說明 |
91
+ |----------|------|
92
+ | `CommonConst.AddExLog` ("Add:ExLog") | 記錄例外 Log |
93
+ | `CommonConst.SendErrorMail` ("Send:ErrorMail") | 發送錯誤通知郵件 |
94
+
95
+ ### 使用者事件(SysUserEventTypeEnum)
96
+ | 事件 | 說明 |
97
+ |------|------|
98
+ | SysUserEventTypeEnum.Add | 使用者建立 |
99
+ | SysUserEventTypeEnum.Register | 使用者自行注冊 |
100
+ | SysUserEventTypeEnum.Update | 使用者更新 |
101
+ | SysUserEventTypeEnum.Delete | 使用者刪除 |
102
+ | SysUserEventTypeEnum.SetStatus | 狀態變更 |
103
+ | SysUserEventTypeEnum.UpdateRole | 角色指派 |
104
+ | SysUserEventTypeEnum.UnlockLogin | 解鎖登入 |
105
+
106
+ ## 事件儲存設定(EventBus.json)
107
+
108
+ | 設定 | 說明 |
109
+ |------|------|
110
+ | InMemory | 記憶體(預設,不持久化) |
111
+ | Redis | Redis 佇列(需要 RedisEventSourceStorer) |
112
+ | RabbitMQ | RabbitMQ 訊息佇列 |
113
+
114
+ ## 最佳實踐
115
+
116
+ 1. **在訂閱者中取得 Scoped 服務**:使用 IServiceScopeFactory.CreateScope()(因訂閱者是 Singleton)
117
+ 2. **Payload 型別**:發布和訂閱要使用相同型別,或用 JSON 字串中轉
118
+ 3. **例外處理**:訂閱者的例外不會傳播到發布者,需要在訂閱者中自行 try/catch
119
+ 4. **避免循環事件**:A 事件觸發 B 事件觸發 A 事件會造成無限循環
120
+
121
+ ## 檔案位置
122
+ - 核心事件訂閱:`Admin.NET.Core/EventBus/AppEventSubscriber.cs`
123
+ - 應用層事件:`Admin.NET.Application/EventBus/`
124
+ - 常數定義:`Admin.NET.Core/Const/CommonConst.cs`
@@ -0,0 +1,136 @@
1
+ # Admin.NET 插件系統指南
2
+
3
+ ## 插件載入機制
4
+
5
+ 插件是獨立的 .NET 專案,透過 App.json 中的 ExternalAssemblies 設定自動載入:
6
+
7
+ ```json
8
+ // Admin.NET.Application/Configuration/App.json
9
+ {
10
+ "App": {
11
+ "ExternalAssemblies": ["plugins"]
12
+ }
13
+ }
14
+ ```
15
+
16
+ Furion 掃描 plugins 目錄中的所有組件,自動發現並載入 [AppStartup] 類別。
17
+
18
+ ## Startup 類別模式
19
+
20
+ 每個插件必須有一個 Startup 類別:
21
+
22
+ ```csharp
23
+ namespace Admin.NET.Plugin.MyFeature;
24
+
25
+ [AppStartup(200)] // 數字越小,越早執行
26
+ public class Startup : AppStartup
27
+ {
28
+ public void ConfigureServices(IServiceCollection services)
29
+ {
30
+ // 註冊插件的 DI 服務
31
+ services.AddSingleton<IMyService, MyService>();
32
+
33
+ // 設定插件選項
34
+ services.AddConfigurableOptions<MyFeatureOptions>();
35
+ }
36
+
37
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
38
+ {
39
+ // 中介軟體設定
40
+ app.UseMyFeatureMiddleware();
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## 執行優先順序
46
+
47
+ | Startup 類別 | 優先順序 | 說明 |
48
+ |-------------|----------|------|
49
+ | Admin.NET.Web.Core/Startup | int.MaxValue | 最先執行(框架核心) |
50
+ | Admin.NET.Application/Startup | 100 | 應用層設定 |
51
+ | 各插件 Startup | 自訂(1~99) | 數字越大越晚執行 |
52
+
53
+ **規則**:數字越大的 [AppStartup(N)] 越晚執行(與直覺相反)。
54
+ 框架核心用 MaxValue 確保最先執行,應用層用 100,插件通常用 1-99。
55
+
56
+ ## 插件目錄結構
57
+
58
+ ```
59
+ Plugins/
60
+ └── Admin.NET.Plugin.MyFeature/
61
+ ├── Admin.NET.Plugin.MyFeature.csproj
62
+ ├── Startup.cs
63
+ ├── Entity/
64
+ │ └── MyEntity.cs
65
+ ├── Service/
66
+ │ └── MyService.cs
67
+ └── Option/
68
+ └── MyFeatureOptions.cs
69
+ ```
70
+
71
+ ## .csproj 設定
72
+
73
+ ```xml
74
+ <Project Sdk="Microsoft.NET.Sdk">
75
+ <PropertyGroup>
76
+ <TargetFramework>net8.0</TargetFramework>
77
+ </PropertyGroup>
78
+ <ItemGroup>
79
+ <!-- 只需引用 Admin.NET.Core -->
80
+ <ProjectReference Include="..\\..\\Admin.NET.Core\\Admin.NET.Core.csproj" />
81
+ </ItemGroup>
82
+ </Project>
83
+ ```
84
+
85
+ ## 現有插件清單
86
+
87
+ | 插件 | 說明 |
88
+ |------|------|
89
+ | Admin.NET.Plugin.GoView | 低代碼視覺化大屏 |
90
+ | Admin.NET.Plugin.DingTalk | 釘釘整合 |
91
+ | Admin.NET.Plugin.ReZero | 介面零程式碼配置 |
92
+ | Admin.NET.Plugin.ApprovalFlow | 審批流程 |
93
+ | Admin.NET.Plugin.K3Cloud | 金蝶 K3 Cloud 整合 |
94
+ | Admin.NET.Plugin.WorkWeixin | 企業微信整合 |
95
+
96
+ ## 插件間通訊
97
+
98
+ 插件之間透過 DI 介面通訊(不直接引用):
99
+
100
+ ```csharp
101
+ // 定義介面(放在共用的 Core 層或介面插件)
102
+ public interface IApprovalService
103
+ {
104
+ Task<bool> SubmitForApproval(long entityId, string entityType);
105
+ }
106
+
107
+ // 在其他插件/服務中使用
108
+ public class OrderService : IDynamicApiController, ITransient
109
+ {
110
+ private readonly IApprovalService _approvalService;
111
+
112
+ public OrderService(IApprovalService approvalService)
113
+ {
114
+ _approvalService = approvalService;
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## 開發新插件步驟
120
+
121
+ 1. 在 `Plugins/` 建立新專案 `Admin.NET.Plugin.{FeatureName}`
122
+ 2. `.csproj` 引用 `Admin.NET.Core`
123
+ 3. 建立 `Startup.cs`(繼承 AppStartup,加上 [AppStartup(N)])
124
+ 4. 在 `ConfigureServices` 中註冊所需服務
125
+ 5. 建立 Entity、Service、Option 等類別
126
+ 6. 確認輸出目錄在 `Admin.NET.Web.Entry/plugins/` 中(或設定 post-build copy)
127
+
128
+ ## 注意事項
129
+
130
+ - 插件不可相互直接引用(防止循環依賴)
131
+ - 業務邏輯變更應放插件,不要修改 Admin.NET.Core(方便升級核心)
132
+ - 插件的實體會自動被 SqlSugar 掃描並建立資料表(依 SeedData 設定)
133
+
134
+ ## 檔案位置
135
+ - 插件目錄:`Admin.NET/Plugins/`
136
+ - App.json:`Admin.NET.Application/Configuration/App.json`
@@ -0,0 +1,146 @@
1
+ # Admin.NET 服務(Service)設計指南
2
+
3
+ ## IDynamicApiController 模式
4
+
5
+ Admin.NET 使用 Furion 的動態 API Controller,Service 同時作為業務邏輯層和 API 端點。
6
+
7
+ ```csharp
8
+ [ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
9
+ public class OrderService : IDynamicApiController, ITransient
10
+ {
11
+ private readonly SqlSugarRepository<Order> _orderRep;
12
+
13
+ public OrderService(SqlSugarRepository<Order> orderRep)
14
+ {
15
+ _orderRep = orderRep;
16
+ }
17
+
18
+ [DisplayName("分頁查詢訂單")]
19
+ [ApiDescriptionSettings(Name = "Page"), HttpPost]
20
+ public async Task<SqlSugarPagedList<OrderOutput>> Page(PageOrderInput input)
21
+ {
22
+ // ...
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## DI 生命週期
28
+
29
+ | 介面 | 生命週期 | 說明 |
30
+ |------|----------|------|
31
+ | `ITransient` | Scoped(每次請求新實例) | 最常用,業務服務預設使用 |
32
+ | `ISingleton` | Singleton(應用程式存活期間唯一) | 快取、設定等無狀態服務 |
33
+
34
+ ## 關鍵 Attribute
35
+
36
+ ### [ApiDescriptionSettings]
37
+ 控制路由和 Swagger 分組:
38
+ ```csharp
39
+ [ApiDescriptionSettings(
40
+ GroupName, // API 分組(通常用 ApplicationConst.GroupName)
41
+ Name = "Page", // 自訂路由片段(預設為方法名稱)
42
+ Order = 100 // Swagger 排序
43
+ )]
44
+ ```
45
+ **路由格式**:`/api/{ClassName去掉Service}/{Name}`
46
+ 例:OrderService + Name="Page" → `POST /api/order/page`
47
+
48
+ ### [DisplayName]
49
+ Swagger 操作說明(必填,用於中文描述):
50
+ ```csharp
51
+ [DisplayName("新增訂單")]
52
+ ```
53
+
54
+ ### [UnitOfWork]
55
+ 自動開啟/提交/回滾交易:
56
+ ```csharp
57
+ [UnitOfWork]
58
+ public async Task CreateOrderWithItems(CreateOrderInput input)
59
+ {
60
+ // 多個 DB 操作,任何例外都會自動回滾
61
+ await _orderRep.InsertAsync(order);
62
+ await _itemRep.InsertRangeAsync(items);
63
+ }
64
+ ```
65
+
66
+ ### HTTP 方法
67
+ ```csharp
68
+ [HttpGet] // GET 請求
69
+ [HttpPost] // POST 請求(預設)
70
+ ```
71
+
72
+ ## BaseService<TEntity> 泛型 CRUD
73
+
74
+ 繼承 BaseService<T> 可直接獲得標準 CRUD,無需自行實作:
75
+
76
+ ```csharp
77
+ public class OrderService : BaseService<Order>, IDynamicApiController, ITransient
78
+ {
79
+ // 自動繼承以下方法:
80
+ // GET /api/order/detail?id=xxx → GetDetail(long id)
81
+ // GET /api/order/list → GetList()
82
+ // POST /api/order/add → Add(Order entity)
83
+ // POST /api/order/update → Update(Order entity)
84
+ // POST /api/order/delete → Delete(long id)
85
+ }
86
+ ```
87
+
88
+ BaseService<T> 方法:
89
+ | 方法 | HTTP | 路由 | 說明 |
90
+ |------|------|------|------|
91
+ | GetDetail(long id) | GET | /detail | 取得單筆 |
92
+ | GetList() | GET | /list | 取得全部 |
93
+ | Add(T entity) | POST | /add | 新增 |
94
+ | Update(T entity) | POST | /update | 更新 |
95
+ | Delete(long id) | POST | /delete | 刪除 |
96
+
97
+ ## DTO 命名慣例
98
+
99
+ ```
100
+ Add{Entity}Input — 新增用 DTO(繼承 Base{Entity}Input)
101
+ Update{Entity}Input — 更新用 DTO(繼承 Base{Entity}Input)
102
+ {Entity}Output — 查詢回傳 DTO
103
+ Page{Entity}Input — 分頁查詢輸入(含 Page、PageSize、Keyword)
104
+ Query{Entity}Input — 單一查詢(含 Id)
105
+ ```
106
+
107
+ ## Mapster 物件映射
108
+
109
+ ```csharp
110
+ // DTO → Entity
111
+ var entity = input.Adapt<Order>();
112
+
113
+ // 更新現有實體
114
+ input.Adapt(existingOrder);
115
+
116
+ // Entity → Output DTO
117
+ var output = entity.Adapt<OrderOutput>();
118
+ ```
119
+
120
+ ## 分頁查詢範例
121
+
122
+ ```csharp
123
+ [DisplayName("分頁查詢")]
124
+ [ApiDescriptionSettings(Name = "Page"), HttpPost]
125
+ public async Task<SqlSugarPagedList<OrderOutput>> Page(PageOrderInput input)
126
+ {
127
+ return await _orderRep.AsQueryable()
128
+ .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword),
129
+ u => u.OrderNo.Contains(input.Keyword))
130
+ .Select<OrderOutput>()
131
+ .ToPagedListAsync(input.Page, input.PageSize);
132
+ }
133
+ ```
134
+
135
+ ## 取得目前使用者資訊
136
+
137
+ ```csharp
138
+ // 從 JWT Claims 取得
139
+ var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
140
+ var tenantId = App.User?.FindFirst(ClaimConst.TenantId)?.Value;
141
+ var realName = App.User?.FindFirst(ClaimConst.RealName)?.Value;
142
+ ```
143
+
144
+ ## 檔案位置
145
+ - BaseService:`Admin.NET.Core/Service/BaseService.cs`
146
+ - 業務服務範例:`Admin.NET.Application/Service/`