@elf-express/admin-net-mcp 1.0.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.
- package/README.md +72 -0
- package/dist/index.js +152 -0
- package/dist/knowledge/attributes.js +158 -0
- package/dist/knowledge/config.js +152 -0
- package/dist/knowledge/entity.js +120 -0
- package/dist/knowledge/event.js +129 -0
- package/dist/knowledge/plugin.js +141 -0
- package/dist/knowledge/service.js +151 -0
- package/dist/knowledge/sqlsugar.js +177 -0
- package/dist/knowledge/vue-typescript.js +343 -0
- package/package.json +34 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.eventGuide = void 0;
|
|
4
|
+
exports.eventGuide = `
|
|
5
|
+
# Admin.NET 事件匯流排指南
|
|
6
|
+
|
|
7
|
+
## 基本概念
|
|
8
|
+
|
|
9
|
+
Admin.NET 使用 Furion 的事件匯流排,透過 [EventSubscribe] 屬性訂閱事件,解耦業務邏輯。
|
|
10
|
+
|
|
11
|
+
## 訂閱者模式
|
|
12
|
+
|
|
13
|
+
\`\`\`csharp
|
|
14
|
+
public class OrderEventSubscriber : IEventSubscriber, ISingleton, IDisposable
|
|
15
|
+
{
|
|
16
|
+
private readonly IServiceScopeFactory _scopeFactory;
|
|
17
|
+
|
|
18
|
+
public OrderEventSubscriber(IServiceScopeFactory scopeFactory)
|
|
19
|
+
{
|
|
20
|
+
_scopeFactory = scopeFactory;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[EventSubscribe("Order:Created")]
|
|
24
|
+
public async Task OnOrderCreated(EventHandlerExecutingContext context)
|
|
25
|
+
{
|
|
26
|
+
// context.Source.EventId — 事件名稱
|
|
27
|
+
// context.Source.Payload — 事件資料(object)
|
|
28
|
+
var order = context.Source.Payload as Order;
|
|
29
|
+
|
|
30
|
+
using var scope = _scopeFactory.CreateScope();
|
|
31
|
+
var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
|
|
32
|
+
await emailService.SendOrderConfirmation(order);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
[EventSubscribe("Order:Cancelled")]
|
|
36
|
+
public async Task OnOrderCancelled(EventHandlerExecutingContext context)
|
|
37
|
+
{
|
|
38
|
+
var payload = (OrderCancelledPayload)context.Source.Payload;
|
|
39
|
+
// 處理取消邏輯
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public void Dispose() { }
|
|
43
|
+
}
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
## 必要條件
|
|
47
|
+
|
|
48
|
+
| 項目 | 說明 |
|
|
49
|
+
|------|------|
|
|
50
|
+
| \`IEventSubscriber\` | 必須實作此介面 |
|
|
51
|
+
| \`ISingleton\` | 通常使用 Singleton(也可 ITransient) |
|
|
52
|
+
| \`IDisposable\` | 建議實作,釋放資源 |
|
|
53
|
+
| \`IServiceScopeFactory\` | 訂閱者是 Singleton,需透過 Scope 使用 Scoped 服務 |
|
|
54
|
+
|
|
55
|
+
## 發布事件
|
|
56
|
+
|
|
57
|
+
### 方式一:非同步發布(推薦)
|
|
58
|
+
\`\`\`csharp
|
|
59
|
+
await App.GetRequiredService<IEventPublisher>().PublishAsync(
|
|
60
|
+
new ChannelEventSource("Order:Created", order));
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
### 方式二:在 AOP / Filter 中發布
|
|
64
|
+
\`\`\`csharp
|
|
65
|
+
await App.PublishAsync(new EventSource
|
|
66
|
+
{
|
|
67
|
+
EventId = CommonConst.AddExLog,
|
|
68
|
+
Payload = logEntity
|
|
69
|
+
});
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
### 方式三:透過注入的 IEventPublisher
|
|
73
|
+
\`\`\`csharp
|
|
74
|
+
public class OrderService : IDynamicApiController, ITransient
|
|
75
|
+
{
|
|
76
|
+
private readonly IEventPublisher _eventPublisher;
|
|
77
|
+
|
|
78
|
+
public OrderService(IEventPublisher eventPublisher)
|
|
79
|
+
{
|
|
80
|
+
_eventPublisher = eventPublisher;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async Task CreateOrder(CreateOrderInput input)
|
|
84
|
+
{
|
|
85
|
+
var order = await SaveOrder(input);
|
|
86
|
+
await _eventPublisher.PublishAsync("Order:Created", order);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
## 系統內建事件
|
|
92
|
+
|
|
93
|
+
### CommonConst 定義的事件
|
|
94
|
+
| 事件名稱 | 說明 |
|
|
95
|
+
|----------|------|
|
|
96
|
+
| \`CommonConst.AddExLog\` ("Add:ExLog") | 記錄例外 Log |
|
|
97
|
+
| \`CommonConst.SendErrorMail\` ("Send:ErrorMail") | 發送錯誤通知郵件 |
|
|
98
|
+
|
|
99
|
+
### 使用者事件(SysUserEventTypeEnum)
|
|
100
|
+
| 事件 | 說明 |
|
|
101
|
+
|------|------|
|
|
102
|
+
| SysUserEventTypeEnum.Add | 使用者建立 |
|
|
103
|
+
| SysUserEventTypeEnum.Register | 使用者自行注冊 |
|
|
104
|
+
| SysUserEventTypeEnum.Update | 使用者更新 |
|
|
105
|
+
| SysUserEventTypeEnum.Delete | 使用者刪除 |
|
|
106
|
+
| SysUserEventTypeEnum.SetStatus | 狀態變更 |
|
|
107
|
+
| SysUserEventTypeEnum.UpdateRole | 角色指派 |
|
|
108
|
+
| SysUserEventTypeEnum.UnlockLogin | 解鎖登入 |
|
|
109
|
+
|
|
110
|
+
## 事件儲存設定(EventBus.json)
|
|
111
|
+
|
|
112
|
+
| 設定 | 說明 |
|
|
113
|
+
|------|------|
|
|
114
|
+
| InMemory | 記憶體(預設,不持久化) |
|
|
115
|
+
| Redis | Redis 佇列(需要 RedisEventSourceStorer) |
|
|
116
|
+
| RabbitMQ | RabbitMQ 訊息佇列 |
|
|
117
|
+
|
|
118
|
+
## 最佳實踐
|
|
119
|
+
|
|
120
|
+
1. **在訂閱者中取得 Scoped 服務**:使用 IServiceScopeFactory.CreateScope()(因訂閱者是 Singleton)
|
|
121
|
+
2. **Payload 型別**:發布和訂閱要使用相同型別,或用 JSON 字串中轉
|
|
122
|
+
3. **例外處理**:訂閱者的例外不會傳播到發布者,需要在訂閱者中自行 try/catch
|
|
123
|
+
4. **避免循環事件**:A 事件觸發 B 事件觸發 A 事件會造成無限循環
|
|
124
|
+
|
|
125
|
+
## 檔案位置
|
|
126
|
+
- 核心事件訂閱:\`Admin.NET.Core/EventBus/AppEventSubscriber.cs\`
|
|
127
|
+
- 應用層事件:\`Admin.NET.Application/EventBus/\`
|
|
128
|
+
- 常數定義:\`Admin.NET.Core/Const/CommonConst.cs\`
|
|
129
|
+
`;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pluginGuide = void 0;
|
|
4
|
+
exports.pluginGuide = `
|
|
5
|
+
# Admin.NET 插件系統指南
|
|
6
|
+
|
|
7
|
+
## 插件載入機制
|
|
8
|
+
|
|
9
|
+
插件是獨立的 .NET 專案,透過 App.json 中的 ExternalAssemblies 設定自動載入:
|
|
10
|
+
|
|
11
|
+
\`\`\`json
|
|
12
|
+
// Admin.NET.Application/Configuration/App.json
|
|
13
|
+
{
|
|
14
|
+
"App": {
|
|
15
|
+
"ExternalAssemblies": ["plugins"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
\`\`\`
|
|
19
|
+
|
|
20
|
+
Furion 掃描 plugins 目錄中的所有組件,自動發現並載入 [AppStartup] 類別。
|
|
21
|
+
|
|
22
|
+
## Startup 類別模式
|
|
23
|
+
|
|
24
|
+
每個插件必須有一個 Startup 類別:
|
|
25
|
+
|
|
26
|
+
\`\`\`csharp
|
|
27
|
+
namespace Admin.NET.Plugin.MyFeature;
|
|
28
|
+
|
|
29
|
+
[AppStartup(200)] // 數字越小,越早執行
|
|
30
|
+
public class Startup : AppStartup
|
|
31
|
+
{
|
|
32
|
+
public void ConfigureServices(IServiceCollection services)
|
|
33
|
+
{
|
|
34
|
+
// 註冊插件的 DI 服務
|
|
35
|
+
services.AddSingleton<IMyService, MyService>();
|
|
36
|
+
|
|
37
|
+
// 設定插件選項
|
|
38
|
+
services.AddConfigurableOptions<MyFeatureOptions>();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
|
42
|
+
{
|
|
43
|
+
// 中介軟體設定
|
|
44
|
+
app.UseMyFeatureMiddleware();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
## 執行優先順序
|
|
50
|
+
|
|
51
|
+
| Startup 類別 | 優先順序 | 說明 |
|
|
52
|
+
|-------------|----------|------|
|
|
53
|
+
| Admin.NET.Web.Core/Startup | int.MaxValue | 最先執行(框架核心) |
|
|
54
|
+
| Admin.NET.Application/Startup | 100 | 應用層設定 |
|
|
55
|
+
| 各插件 Startup | 自訂(1~99) | 數字越大越晚執行 |
|
|
56
|
+
|
|
57
|
+
**規則**:數字越大的 [AppStartup(N)] 越晚執行(與直覺相反)。
|
|
58
|
+
框架核心用 MaxValue 確保最先執行,應用層用 100,插件通常用 1-99。
|
|
59
|
+
|
|
60
|
+
## 插件目錄結構
|
|
61
|
+
|
|
62
|
+
\`\`\`
|
|
63
|
+
Plugins/
|
|
64
|
+
└── Admin.NET.Plugin.MyFeature/
|
|
65
|
+
├── Admin.NET.Plugin.MyFeature.csproj
|
|
66
|
+
├── Startup.cs
|
|
67
|
+
├── Entity/
|
|
68
|
+
│ └── MyEntity.cs
|
|
69
|
+
├── Service/
|
|
70
|
+
│ └── MyService.cs
|
|
71
|
+
└── Option/
|
|
72
|
+
└── MyFeatureOptions.cs
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
## .csproj 設定
|
|
76
|
+
|
|
77
|
+
\`\`\`xml
|
|
78
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
79
|
+
<PropertyGroup>
|
|
80
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
81
|
+
</PropertyGroup>
|
|
82
|
+
<ItemGroup>
|
|
83
|
+
<!-- 只需引用 Admin.NET.Core -->
|
|
84
|
+
<ProjectReference Include="..\\..\\Admin.NET.Core\\Admin.NET.Core.csproj" />
|
|
85
|
+
</ItemGroup>
|
|
86
|
+
</Project>
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
## 現有插件清單
|
|
90
|
+
|
|
91
|
+
| 插件 | 說明 |
|
|
92
|
+
|------|------|
|
|
93
|
+
| Admin.NET.Plugin.GoView | 低代碼視覺化大屏 |
|
|
94
|
+
| Admin.NET.Plugin.DingTalk | 釘釘整合 |
|
|
95
|
+
| Admin.NET.Plugin.ReZero | 介面零程式碼配置 |
|
|
96
|
+
| Admin.NET.Plugin.ApprovalFlow | 審批流程 |
|
|
97
|
+
| Admin.NET.Plugin.K3Cloud | 金蝶 K3 Cloud 整合 |
|
|
98
|
+
| Admin.NET.Plugin.WorkWeixin | 企業微信整合 |
|
|
99
|
+
|
|
100
|
+
## 插件間通訊
|
|
101
|
+
|
|
102
|
+
插件之間透過 DI 介面通訊(不直接引用):
|
|
103
|
+
|
|
104
|
+
\`\`\`csharp
|
|
105
|
+
// 定義介面(放在共用的 Core 層或介面插件)
|
|
106
|
+
public interface IApprovalService
|
|
107
|
+
{
|
|
108
|
+
Task<bool> SubmitForApproval(long entityId, string entityType);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 在其他插件/服務中使用
|
|
112
|
+
public class OrderService : IDynamicApiController, ITransient
|
|
113
|
+
{
|
|
114
|
+
private readonly IApprovalService _approvalService;
|
|
115
|
+
|
|
116
|
+
public OrderService(IApprovalService approvalService)
|
|
117
|
+
{
|
|
118
|
+
_approvalService = approvalService;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
## 開發新插件步驟
|
|
124
|
+
|
|
125
|
+
1. 在 \`Plugins/\` 建立新專案 \`Admin.NET.Plugin.{FeatureName}\`
|
|
126
|
+
2. \`.csproj\` 引用 \`Admin.NET.Core\`
|
|
127
|
+
3. 建立 \`Startup.cs\`(繼承 AppStartup,加上 [AppStartup(N)])
|
|
128
|
+
4. 在 \`ConfigureServices\` 中註冊所需服務
|
|
129
|
+
5. 建立 Entity、Service、Option 等類別
|
|
130
|
+
6. 確認輸出目錄在 \`Admin.NET.Web.Entry/plugins/\` 中(或設定 post-build copy)
|
|
131
|
+
|
|
132
|
+
## 注意事項
|
|
133
|
+
|
|
134
|
+
- 插件不可相互直接引用(防止循環依賴)
|
|
135
|
+
- 業務邏輯變更應放插件,不要修改 Admin.NET.Core(方便升級核心)
|
|
136
|
+
- 插件的實體會自動被 SqlSugar 掃描並建立資料表(依 SeedData 設定)
|
|
137
|
+
|
|
138
|
+
## 檔案位置
|
|
139
|
+
- 插件目錄:\`Admin.NET/Plugins/\`
|
|
140
|
+
- App.json:\`Admin.NET.Application/Configuration/App.json\`
|
|
141
|
+
`;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serviceGuide = void 0;
|
|
4
|
+
exports.serviceGuide = `
|
|
5
|
+
# Admin.NET 服務(Service)設計指南
|
|
6
|
+
|
|
7
|
+
## IDynamicApiController 模式
|
|
8
|
+
|
|
9
|
+
Admin.NET 使用 Furion 的動態 API Controller,Service 同時作為業務邏輯層和 API 端點。
|
|
10
|
+
|
|
11
|
+
\`\`\`csharp
|
|
12
|
+
[ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
|
|
13
|
+
public class OrderService : IDynamicApiController, ITransient
|
|
14
|
+
{
|
|
15
|
+
private readonly SqlSugarRepository<Order> _orderRep;
|
|
16
|
+
|
|
17
|
+
public OrderService(SqlSugarRepository<Order> orderRep)
|
|
18
|
+
{
|
|
19
|
+
_orderRep = orderRep;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[DisplayName("分頁查詢訂單")]
|
|
23
|
+
[ApiDescriptionSettings(Name = "Page"), HttpPost]
|
|
24
|
+
public async Task<SqlSugarPagedList<OrderOutput>> Page(PageOrderInput input)
|
|
25
|
+
{
|
|
26
|
+
// ...
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
## DI 生命週期
|
|
32
|
+
|
|
33
|
+
| 介面 | 生命週期 | 說明 |
|
|
34
|
+
|------|----------|------|
|
|
35
|
+
| \`ITransient\` | Scoped(每次請求新實例) | 最常用,業務服務預設使用 |
|
|
36
|
+
| \`ISingleton\` | Singleton(應用程式存活期間唯一) | 快取、設定等無狀態服務 |
|
|
37
|
+
|
|
38
|
+
## 關鍵 Attribute
|
|
39
|
+
|
|
40
|
+
### [ApiDescriptionSettings]
|
|
41
|
+
控制路由和 Swagger 分組:
|
|
42
|
+
\`\`\`csharp
|
|
43
|
+
[ApiDescriptionSettings(
|
|
44
|
+
GroupName, // API 分組(通常用 ApplicationConst.GroupName)
|
|
45
|
+
Name = "Page", // 自訂路由片段(預設為方法名稱)
|
|
46
|
+
Order = 100 // Swagger 排序
|
|
47
|
+
)]
|
|
48
|
+
\`\`\`
|
|
49
|
+
**路由格式**:\`/api/{ClassName去掉Service}/{Name}\`
|
|
50
|
+
例:OrderService + Name="Page" → \`POST /api/order/page\`
|
|
51
|
+
|
|
52
|
+
### [DisplayName]
|
|
53
|
+
Swagger 操作說明(必填,用於中文描述):
|
|
54
|
+
\`\`\`csharp
|
|
55
|
+
[DisplayName("新增訂單")]
|
|
56
|
+
\`\`\`
|
|
57
|
+
|
|
58
|
+
### [UnitOfWork]
|
|
59
|
+
自動開啟/提交/回滾交易:
|
|
60
|
+
\`\`\`csharp
|
|
61
|
+
[UnitOfWork]
|
|
62
|
+
public async Task CreateOrderWithItems(CreateOrderInput input)
|
|
63
|
+
{
|
|
64
|
+
// 多個 DB 操作,任何例外都會自動回滾
|
|
65
|
+
await _orderRep.InsertAsync(order);
|
|
66
|
+
await _itemRep.InsertRangeAsync(items);
|
|
67
|
+
}
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
### HTTP 方法
|
|
71
|
+
\`\`\`csharp
|
|
72
|
+
[HttpGet] // GET 請求
|
|
73
|
+
[HttpPost] // POST 請求(預設)
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
## BaseService<TEntity> 泛型 CRUD
|
|
77
|
+
|
|
78
|
+
繼承 BaseService<T> 可直接獲得標準 CRUD,無需自行實作:
|
|
79
|
+
|
|
80
|
+
\`\`\`csharp
|
|
81
|
+
public class OrderService : BaseService<Order>, IDynamicApiController, ITransient
|
|
82
|
+
{
|
|
83
|
+
// 自動繼承以下方法:
|
|
84
|
+
// GET /api/order/detail?id=xxx → GetDetail(long id)
|
|
85
|
+
// GET /api/order/list → GetList()
|
|
86
|
+
// POST /api/order/add → Add(Order entity)
|
|
87
|
+
// POST /api/order/update → Update(Order entity)
|
|
88
|
+
// POST /api/order/delete → Delete(long id)
|
|
89
|
+
}
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
BaseService<T> 方法:
|
|
93
|
+
| 方法 | HTTP | 路由 | 說明 |
|
|
94
|
+
|------|------|------|------|
|
|
95
|
+
| GetDetail(long id) | GET | /detail | 取得單筆 |
|
|
96
|
+
| GetList() | GET | /list | 取得全部 |
|
|
97
|
+
| Add(T entity) | POST | /add | 新增 |
|
|
98
|
+
| Update(T entity) | POST | /update | 更新 |
|
|
99
|
+
| Delete(long id) | POST | /delete | 刪除 |
|
|
100
|
+
|
|
101
|
+
## DTO 命名慣例
|
|
102
|
+
|
|
103
|
+
\`\`\`
|
|
104
|
+
Add{Entity}Input — 新增用 DTO(繼承 Base{Entity}Input)
|
|
105
|
+
Update{Entity}Input — 更新用 DTO(繼承 Base{Entity}Input)
|
|
106
|
+
{Entity}Output — 查詢回傳 DTO
|
|
107
|
+
Page{Entity}Input — 分頁查詢輸入(含 Page、PageSize、Keyword)
|
|
108
|
+
Query{Entity}Input — 單一查詢(含 Id)
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
## Mapster 物件映射
|
|
112
|
+
|
|
113
|
+
\`\`\`csharp
|
|
114
|
+
// DTO → Entity
|
|
115
|
+
var entity = input.Adapt<Order>();
|
|
116
|
+
|
|
117
|
+
// 更新現有實體
|
|
118
|
+
input.Adapt(existingOrder);
|
|
119
|
+
|
|
120
|
+
// Entity → Output DTO
|
|
121
|
+
var output = entity.Adapt<OrderOutput>();
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
## 分頁查詢範例
|
|
125
|
+
|
|
126
|
+
\`\`\`csharp
|
|
127
|
+
[DisplayName("分頁查詢")]
|
|
128
|
+
[ApiDescriptionSettings(Name = "Page"), HttpPost]
|
|
129
|
+
public async Task<SqlSugarPagedList<OrderOutput>> Page(PageOrderInput input)
|
|
130
|
+
{
|
|
131
|
+
return await _orderRep.AsQueryable()
|
|
132
|
+
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword),
|
|
133
|
+
u => u.OrderNo.Contains(input.Keyword))
|
|
134
|
+
.Select<OrderOutput>()
|
|
135
|
+
.ToPagedListAsync(input.Page, input.PageSize);
|
|
136
|
+
}
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
## 取得目前使用者資訊
|
|
140
|
+
|
|
141
|
+
\`\`\`csharp
|
|
142
|
+
// 從 JWT Claims 取得
|
|
143
|
+
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
|
144
|
+
var tenantId = App.User?.FindFirst(ClaimConst.TenantId)?.Value;
|
|
145
|
+
var realName = App.User?.FindFirst(ClaimConst.RealName)?.Value;
|
|
146
|
+
\`\`\`
|
|
147
|
+
|
|
148
|
+
## 檔案位置
|
|
149
|
+
- BaseService:\`Admin.NET.Core/Service/BaseService.cs\`
|
|
150
|
+
- 業務服務範例:\`Admin.NET.Application/Service/\`
|
|
151
|
+
`;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqlsugarGuide = void 0;
|
|
4
|
+
exports.sqlsugarGuide = `
|
|
5
|
+
# Admin.NET SqlSugar ORM 指南
|
|
6
|
+
|
|
7
|
+
## AOP 自動填充欄位
|
|
8
|
+
|
|
9
|
+
SqlSugar AOP 在 Insert/Update 時自動填入以下欄位(無需手動設定):
|
|
10
|
+
|
|
11
|
+
### Insert 時自動填充
|
|
12
|
+
| 欄位 | 來源 | 條件 |
|
|
13
|
+
|------|------|------|
|
|
14
|
+
| Id(long) | YitIdHelper.NextId() | 值為 0 或 null 時 |
|
|
15
|
+
| CreateTime | DateTime.Now | 為 null 或 MinValue 時 |
|
|
16
|
+
| CreateUserId | ClaimConst.UserId(JWT) | 為 null 或 0 時 |
|
|
17
|
+
| CreateUserName | ClaimConst.RealName(JWT) | 為空字串時 |
|
|
18
|
+
| TenantId | ClaimConst.TenantId(JWT) | 為 null 或 0,且實體實作 ITenantIdFilter |
|
|
19
|
+
|
|
20
|
+
### Update 時自動填充
|
|
21
|
+
| 欄位 | 來源 |
|
|
22
|
+
|------|------|
|
|
23
|
+
| UpdateTime | DateTime.Now(永遠更新) |
|
|
24
|
+
| UpdateUserId | 目前使用者 ID |
|
|
25
|
+
| UpdateUserName | 目前使用者姓名 |
|
|
26
|
+
| DeleteTime | DateTime.Now(當 IsDelete=true) |
|
|
27
|
+
|
|
28
|
+
**注意**:種子資料初始化時(\_isHandlingSeedData=true)AOP 會跳過,使用明確設定的值。
|
|
29
|
+
|
|
30
|
+
## 自動查詢過濾
|
|
31
|
+
|
|
32
|
+
SqlSugar 根據實體實作的介面自動加入 WHERE 條件:
|
|
33
|
+
|
|
34
|
+
\`\`\`csharp
|
|
35
|
+
// 軟刪除過濾(實作 IDeletedFilter 的實體自動套用)
|
|
36
|
+
WHERE IsDelete = 0
|
|
37
|
+
|
|
38
|
+
// 租戶過濾(實作 ITenantIdFilter 的實體自動套用)
|
|
39
|
+
WHERE TenantId = 目前使用者的 TenantId
|
|
40
|
+
|
|
41
|
+
// 組織資料權限(實作 IOrgIdFilter 的實體自動套用)
|
|
42
|
+
WHERE OrgId IN (使用者有權限的組織清單)
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
**SuperAdmin 例外**:DbConnectionOptions.SuperAdminIgnoreIDeletedFilter = true 時,超級管理員可看到已刪除資料。
|
|
46
|
+
|
|
47
|
+
## SqlSugarRepository<T> 使用方式
|
|
48
|
+
|
|
49
|
+
### 注入
|
|
50
|
+
\`\`\`csharp
|
|
51
|
+
public class OrderService : IDynamicApiController, ITransient
|
|
52
|
+
{
|
|
53
|
+
private readonly SqlSugarRepository<Order> _orderRep;
|
|
54
|
+
|
|
55
|
+
public OrderService(SqlSugarRepository<Order> orderRep)
|
|
56
|
+
{
|
|
57
|
+
_orderRep = orderRep;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
### 常用查詢
|
|
63
|
+
\`\`\`csharp
|
|
64
|
+
// 取得單筆
|
|
65
|
+
var order = await _orderRep.GetFirstAsync(u => u.Id == id);
|
|
66
|
+
|
|
67
|
+
// 查詢清單
|
|
68
|
+
var list = await _orderRep.GetListAsync(u => u.Status == 1);
|
|
69
|
+
|
|
70
|
+
// 分頁(推薦)
|
|
71
|
+
var paged = await _orderRep.AsQueryable()
|
|
72
|
+
.WhereIF(!string.IsNullOrWhiteSpace(keyword), u => u.Name.Contains(keyword))
|
|
73
|
+
.OrderBy(u => u.CreateTime, OrderByType.Desc)
|
|
74
|
+
.ToPagedListAsync(page, pageSize);
|
|
75
|
+
|
|
76
|
+
// 判斷是否存在
|
|
77
|
+
var exists = await _orderRep.IsAnyAsync(u => u.OrderNo == orderNo);
|
|
78
|
+
|
|
79
|
+
// 計數
|
|
80
|
+
var count = await _orderRep.CountAsync(u => u.Status == 1);
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
### 新增/更新/刪除
|
|
84
|
+
\`\`\`csharp
|
|
85
|
+
// 新增
|
|
86
|
+
await _orderRep.InsertAsync(order);
|
|
87
|
+
await _orderRep.InsertRangeAsync(orders);
|
|
88
|
+
|
|
89
|
+
// 更新(整筆)
|
|
90
|
+
await _orderRep.UpdateAsync(order);
|
|
91
|
+
|
|
92
|
+
// 部分欄位更新
|
|
93
|
+
await _orderRep.UpdateAsync(
|
|
94
|
+
u => new Order { Status = 2, UpdateTime = DateTime.Now },
|
|
95
|
+
u => u.Id == id);
|
|
96
|
+
|
|
97
|
+
// 軟刪除(需實作 IDeletedFilter)
|
|
98
|
+
await _orderRep.UpdateAsync(
|
|
99
|
+
u => new Order { IsDelete = true },
|
|
100
|
+
u => u.Id == id);
|
|
101
|
+
|
|
102
|
+
// 真實刪除
|
|
103
|
+
await _orderRep.DeleteAsync(id);
|
|
104
|
+
\`\`\`
|
|
105
|
+
|
|
106
|
+
### 聯表查詢
|
|
107
|
+
\`\`\`csharp
|
|
108
|
+
var result = await _orderRep.AsQueryable()
|
|
109
|
+
.LeftJoin<OrderItem>((o, i) => o.Id == i.OrderId)
|
|
110
|
+
.Select((o, i) => new OrderWithItemsOutput
|
|
111
|
+
{
|
|
112
|
+
OrderNo = o.OrderNo,
|
|
113
|
+
ItemCount = SqlFunc.AggregateCount(i.Id)
|
|
114
|
+
})
|
|
115
|
+
.ToListAsync();
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
## 交易管理(UnitOfWork)
|
|
119
|
+
|
|
120
|
+
### 方式一:[UnitOfWork] Attribute(推薦)
|
|
121
|
+
\`\`\`csharp
|
|
122
|
+
[UnitOfWork]
|
|
123
|
+
public async Task CreateOrderWithItems(CreateOrderInput input)
|
|
124
|
+
{
|
|
125
|
+
var order = input.Adapt<Order>();
|
|
126
|
+
await _orderRep.InsertAsync(order);
|
|
127
|
+
// 例外時自動回滾
|
|
128
|
+
await _itemRep.InsertRangeAsync(items);
|
|
129
|
+
}
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
### 方式二:手動交易
|
|
133
|
+
\`\`\`csharp
|
|
134
|
+
var db = _orderRep.Context;
|
|
135
|
+
try
|
|
136
|
+
{
|
|
137
|
+
db.BeginTran();
|
|
138
|
+
await _orderRep.InsertAsync(order);
|
|
139
|
+
await _itemRep.InsertRangeAsync(items);
|
|
140
|
+
db.CommitTran();
|
|
141
|
+
}
|
|
142
|
+
catch
|
|
143
|
+
{
|
|
144
|
+
db.RollbackTran();
|
|
145
|
+
throw;
|
|
146
|
+
}
|
|
147
|
+
\`\`\`
|
|
148
|
+
|
|
149
|
+
## 多資料庫路由
|
|
150
|
+
|
|
151
|
+
Repository 依實體 Attribute 自動選擇連線:
|
|
152
|
+
1. \`[TenantAttribute]\` → 租戶專屬 DB
|
|
153
|
+
2. \`[LogTableAttribute]\` → Log 專用 DB
|
|
154
|
+
3. \`[SysTableAttribute]\` → 主 DB(強制)
|
|
155
|
+
4. Request Header 中的 TenantId → 租戶 DB
|
|
156
|
+
5. 預設 → 主 DB
|
|
157
|
+
|
|
158
|
+
## 分表(Split Table)方法
|
|
159
|
+
|
|
160
|
+
用於日期分表或大型資料集:
|
|
161
|
+
\`\`\`csharp
|
|
162
|
+
await _logRep.SplitTableInsertAsync(logEntry);
|
|
163
|
+
var logs = await _logRep.SplitTableGetListAsync(u => u.CreateTime > startDate);
|
|
164
|
+
\`\`\`
|
|
165
|
+
|
|
166
|
+
## 原始 SQL(謹慎使用)
|
|
167
|
+
\`\`\`csharp
|
|
168
|
+
var result = await db.SqlQueryable<OrderOutput>("SELECT * FROM Orders WHERE Status=@status")
|
|
169
|
+
.AddParameters(new SugarParameter("@status", 1))
|
|
170
|
+
.ToListAsync();
|
|
171
|
+
\`\`\`
|
|
172
|
+
|
|
173
|
+
## 檔案位置
|
|
174
|
+
- SqlSugar 設定(AOP):\`Admin.NET.Core/SqlSugar/SqlSugarSetup.cs\`
|
|
175
|
+
- Repository 實作:\`Admin.NET.Core/SqlSugar/SqlSugarRepository.cs\`
|
|
176
|
+
- UnitOfWork:\`Admin.NET.Core/SqlSugar/SqlSugarUnitOfWork.cs\`
|
|
177
|
+
`;
|