@ahoo-wang/fetcher-wow 0.9.6 → 0.10.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 +541 -0
- package/README.zh-CN.md +540 -0
- package/dist/command/commandRequest.d.ts +31 -0
- package/dist/command/commandRequest.d.ts.map +1 -0
- package/dist/command/commandResult.d.ts +12 -0
- package/dist/command/commandResult.d.ts.map +1 -0
- package/dist/command/commandResultEventStream.d.ts +14 -0
- package/dist/command/commandResultEventStream.d.ts.map +1 -0
- package/dist/command/index.d.ts +3 -1
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/types.d.ts +4 -2
- package/dist/command/types.d.ts.map +1 -1
- package/dist/index.es.js +176 -161
- package/dist/index.umd.js +1 -1
- package/dist/query/index.d.ts +0 -1
- package/dist/query/index.d.ts.map +1 -1
- package/package.json +3 -4
- package/dist/command/commandGateway.d.ts +0 -3
- package/dist/command/commandGateway.d.ts.map +0 -1
- package/dist/query/snapshotQueryService.d.ts +0 -3
- package/dist/query/snapshotQueryService.d.ts.map +0 -1
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# @ahoo-wang/fetcher-wow
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
|
|
4
|
+
[](https://github.com/Ahoo-Wang/fetcher/actions)
|
|
5
|
+
[](https://codecov.io/gh/Ahoo-Wang/fetcher)
|
|
6
|
+
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
|
+
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
|
|
8
|
+
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
|
|
9
|
+
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
10
|
+
|
|
11
|
+
为 [Wow](https://github.com/Ahoo-Wang/Wow) 框架提供支持。提供用于与 Wow CQRS/DDD 框架配合使用的 TypeScript 类型和工具。
|
|
12
|
+
|
|
13
|
+
## 🌟 特性
|
|
14
|
+
|
|
15
|
+
- **📦 全面的类型定义**:为 Wow 框架实体提供完整的 TypeScript 支持
|
|
16
|
+
- **🔧 命令工具**:用于处理 Wow 命令和命令结果的辅助工具
|
|
17
|
+
- **🔍 查询 DSL**:丰富的查询条件构建器,支持多种操作符
|
|
18
|
+
- **📡 事件流支持**:与服务器发送事件集成,实现实时命令结果
|
|
19
|
+
- **🔄 CQRS 模式**:支持命令查询责任分离模式
|
|
20
|
+
- **🧱 DDD 构建块**:用于聚合、事件等的领域驱动设计类型
|
|
21
|
+
|
|
22
|
+
## 🚀 快速开始
|
|
23
|
+
|
|
24
|
+
### 安装
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 使用 npm
|
|
28
|
+
npm install @ahoo-wang/fetcher-wow
|
|
29
|
+
|
|
30
|
+
# 使用 pnpm
|
|
31
|
+
pnpm add @ahoo-wang/fetcher-wow
|
|
32
|
+
|
|
33
|
+
# 使用 yarn
|
|
34
|
+
yarn add @ahoo-wang/fetcher-wow
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 📚 API 参考
|
|
38
|
+
|
|
39
|
+
### 命令模块
|
|
40
|
+
|
|
41
|
+
#### CommandHeaders
|
|
42
|
+
|
|
43
|
+
Wow 命令处理中使用的标准 HTTP 头部常量:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { CommandHeaders } from '@ahoo-wang/fetcher-wow';
|
|
47
|
+
|
|
48
|
+
// 使用示例
|
|
49
|
+
const request = {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: {
|
|
52
|
+
[CommandHeaders.TENANT_ID]: 'tenant-123',
|
|
53
|
+
[CommandHeaders.AGGREGATE_ID]: 'aggregate-456',
|
|
54
|
+
[CommandHeaders.REQUEST_ID]: 'request-789',
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify(command),
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
关键头部包括:
|
|
61
|
+
|
|
62
|
+
- `TENANT_ID` - 租户标识符
|
|
63
|
+
- `OWNER_ID` - 所有者标识符
|
|
64
|
+
- `AGGREGATE_ID` - 聚合根标识符
|
|
65
|
+
- `AGGREGATE_VERSION` - 预期聚合版本
|
|
66
|
+
- `REQUEST_ID` - 请求跟踪 ID
|
|
67
|
+
- `WAIT_*` - 各种等待条件头部
|
|
68
|
+
- `LOCAL_FIRST` - 本地处理偏好
|
|
69
|
+
- 以及更多...
|
|
70
|
+
|
|
71
|
+
#### CommandRequest
|
|
72
|
+
|
|
73
|
+
具有完整配置选项的命令请求接口:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { CommandRequest, CommandHeaders } from '@ahoo-wang/fetcher-wow';
|
|
77
|
+
|
|
78
|
+
const commandRequest: CommandRequest = {
|
|
79
|
+
path: '/commands/CreateUser',
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
[CommandHeaders.TENANT_ID]: 'tenant-123',
|
|
83
|
+
},
|
|
84
|
+
body: {
|
|
85
|
+
name: 'John Doe',
|
|
86
|
+
email: 'john@example.com',
|
|
87
|
+
},
|
|
88
|
+
timeout: 5000,
|
|
89
|
+
aggregateId: 'user-456',
|
|
90
|
+
requestId: 'req-789',
|
|
91
|
+
localFirst: true,
|
|
92
|
+
stream: false,
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### CommandResult
|
|
97
|
+
|
|
98
|
+
表示命令执行结果的接口:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { CommandResult, CommandStage } from '@ahoo-wang/fetcher-wow';
|
|
102
|
+
|
|
103
|
+
const commandResult: CommandResult = {
|
|
104
|
+
id: 'result-123',
|
|
105
|
+
commandId: 'cmd-456',
|
|
106
|
+
requestId: 'req-789',
|
|
107
|
+
stage: CommandStage.PROCESSED,
|
|
108
|
+
contextName: 'user-context',
|
|
109
|
+
aggregateName: 'User',
|
|
110
|
+
aggregateId: 'user-456',
|
|
111
|
+
aggregateVersion: 1,
|
|
112
|
+
errorCode: 'Ok',
|
|
113
|
+
errorMsg: '',
|
|
114
|
+
function: {
|
|
115
|
+
functionKind: 'COMMAND',
|
|
116
|
+
contextName: 'user-context',
|
|
117
|
+
processorName: 'UserProcessor',
|
|
118
|
+
name: 'CreateUser',
|
|
119
|
+
},
|
|
120
|
+
signalTime: Date.now(),
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### CommandResultEventStream
|
|
125
|
+
|
|
126
|
+
用于处理命令结果事件流的工具:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import {
|
|
130
|
+
toCommandResultEventStream,
|
|
131
|
+
CommandResultEvent,
|
|
132
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
133
|
+
import { fetchEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
134
|
+
|
|
135
|
+
// 将 ServerSentEventStream 转换为 CommandResultEventStream
|
|
136
|
+
const eventStream = fetchEventStream('/commands/stream');
|
|
137
|
+
const commandResultStream = toCommandResultEventStream(eventStream);
|
|
138
|
+
|
|
139
|
+
// 处理实时到达的命令结果
|
|
140
|
+
const reader = commandResultStream.getReader();
|
|
141
|
+
while (true) {
|
|
142
|
+
const { done, value } = await reader.read();
|
|
143
|
+
if (done) break;
|
|
144
|
+
|
|
145
|
+
const commandResult: CommandResult = value.data;
|
|
146
|
+
console.log('命令结果:', commandResult);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 查询模块
|
|
151
|
+
|
|
152
|
+
#### 条件构建器
|
|
153
|
+
|
|
154
|
+
支持操作符的综合查询条件构建器:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import {
|
|
158
|
+
and,
|
|
159
|
+
or,
|
|
160
|
+
eq,
|
|
161
|
+
ne,
|
|
162
|
+
gt,
|
|
163
|
+
lt,
|
|
164
|
+
contains,
|
|
165
|
+
isIn,
|
|
166
|
+
between,
|
|
167
|
+
today,
|
|
168
|
+
active,
|
|
169
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
170
|
+
|
|
171
|
+
// 简单条件
|
|
172
|
+
const simpleConditions = [
|
|
173
|
+
eq('name', 'John'),
|
|
174
|
+
ne('status', 'inactive'),
|
|
175
|
+
gt('age', 18),
|
|
176
|
+
lt('score', 100),
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
// 复杂条件
|
|
180
|
+
const complexCondition = and(
|
|
181
|
+
eq('tenantId', 'tenant-123'),
|
|
182
|
+
or(
|
|
183
|
+
contains('email', '@company.com'),
|
|
184
|
+
isIn('department', 'engineering', 'marketing'),
|
|
185
|
+
),
|
|
186
|
+
between('salary', 50000, 100000),
|
|
187
|
+
today('createdAt'),
|
|
188
|
+
active(),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// 日期条件
|
|
192
|
+
const dateConditions = [
|
|
193
|
+
today('createdAt'),
|
|
194
|
+
beforeToday('lastLogin', 7), // 最近7天内
|
|
195
|
+
thisWeek('updatedAt'),
|
|
196
|
+
lastMonth('createdDate'),
|
|
197
|
+
];
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### 操作符
|
|
201
|
+
|
|
202
|
+
用于查询构建的完整操作符枚举:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { Operator } from '@ahoo-wang/fetcher-wow';
|
|
206
|
+
|
|
207
|
+
// 逻辑操作符
|
|
208
|
+
(Operator.AND, Operator.OR, Operator.NOR);
|
|
209
|
+
|
|
210
|
+
// 比较操作符
|
|
211
|
+
(Operator.EQ,
|
|
212
|
+
Operator.NE,
|
|
213
|
+
Operator.GT,
|
|
214
|
+
Operator.LT,
|
|
215
|
+
Operator.GTE,
|
|
216
|
+
Operator.LTE);
|
|
217
|
+
|
|
218
|
+
// 成员操作符
|
|
219
|
+
(Operator.IN, Operator.NOT_IN, Operator.ALL_IN, Operator.BETWEEN);
|
|
220
|
+
|
|
221
|
+
// 字符串操作符
|
|
222
|
+
(Operator.CONTAINS, Operator.STARTS_WITH, Operator.ENDS_WITH);
|
|
223
|
+
|
|
224
|
+
// 存在性操作符
|
|
225
|
+
(Operator.NULL, Operator.NOT_NULL, Operator.EXISTS);
|
|
226
|
+
|
|
227
|
+
// 布尔操作符
|
|
228
|
+
(Operator.TRUE, Operator.FALSE);
|
|
229
|
+
|
|
230
|
+
// 日期操作符
|
|
231
|
+
(Operator.TODAY,
|
|
232
|
+
Operator.BEFORE_TODAY,
|
|
233
|
+
Operator.TOMORROW,
|
|
234
|
+
Operator.THIS_WEEK,
|
|
235
|
+
Operator.NEXT_WEEK,
|
|
236
|
+
Operator.LAST_WEEK,
|
|
237
|
+
Operator.THIS_MONTH,
|
|
238
|
+
Operator.LAST_MONTH,
|
|
239
|
+
Operator.RECENT_DAYS,
|
|
240
|
+
Operator.EARLIER_DAYS);
|
|
241
|
+
|
|
242
|
+
// 特殊操作符
|
|
243
|
+
(Operator.ID,
|
|
244
|
+
Operator.IDS,
|
|
245
|
+
Operator.AGGREGATE_ID,
|
|
246
|
+
Operator.AGGREGATE_IDS,
|
|
247
|
+
Operator.TENANT_ID,
|
|
248
|
+
Operator.OWNER_ID,
|
|
249
|
+
Operator.DELETED,
|
|
250
|
+
Operator.ALL,
|
|
251
|
+
Operator.ELEM_MATCH,
|
|
252
|
+
Operator.RAW);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### 可查询接口
|
|
256
|
+
|
|
257
|
+
用于构建带排序、分页和投影的查询的接口:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import {
|
|
261
|
+
Queryable,
|
|
262
|
+
SortDirection,
|
|
263
|
+
DEFAULT_PAGINATION,
|
|
264
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
265
|
+
|
|
266
|
+
const query: Queryable = {
|
|
267
|
+
condition: eq('status', 'active'),
|
|
268
|
+
sort: [
|
|
269
|
+
{ field: 'createdAt', direction: SortDirection.DESC },
|
|
270
|
+
{ field: 'name', direction: SortDirection.ASC },
|
|
271
|
+
],
|
|
272
|
+
projection: {
|
|
273
|
+
include: ['id', 'name', 'email', 'status'],
|
|
274
|
+
exclude: ['password', 'internalNotes'],
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const pagedQuery = {
|
|
279
|
+
...query,
|
|
280
|
+
pagination: {
|
|
281
|
+
index: 2,
|
|
282
|
+
size: 20,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### 类型模块
|
|
288
|
+
|
|
289
|
+
#### 核心类型
|
|
290
|
+
|
|
291
|
+
领域建模的基本类型:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import {
|
|
295
|
+
Identifier,
|
|
296
|
+
Version,
|
|
297
|
+
TenantId,
|
|
298
|
+
OwnerId,
|
|
299
|
+
NamedAggregate,
|
|
300
|
+
AggregateId,
|
|
301
|
+
StateCapable,
|
|
302
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
303
|
+
|
|
304
|
+
interface User
|
|
305
|
+
extends Identifier,
|
|
306
|
+
Version,
|
|
307
|
+
TenantId,
|
|
308
|
+
OwnerId,
|
|
309
|
+
NamedAggregate,
|
|
310
|
+
StateCapable<UserState> {
|
|
311
|
+
id: string;
|
|
312
|
+
version: number;
|
|
313
|
+
tenantId: string;
|
|
314
|
+
ownerId: string;
|
|
315
|
+
contextName: string;
|
|
316
|
+
aggregateName: string;
|
|
317
|
+
state: UserState;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
interface UserState {
|
|
321
|
+
name: string;
|
|
322
|
+
email: string;
|
|
323
|
+
status: 'active' | 'inactive';
|
|
324
|
+
createdAt: number;
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### 错误处理
|
|
329
|
+
|
|
330
|
+
标准错误类型和代码:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { ErrorInfo, ErrorCodes, RecoverableType } from '@ahoo-wang/fetcher-wow';
|
|
334
|
+
|
|
335
|
+
const errorInfo: ErrorInfo = {
|
|
336
|
+
errorCode: ErrorCodes.NOT_FOUND,
|
|
337
|
+
errorMsg: '用户未找到',
|
|
338
|
+
bindingErrors: [],
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// 检查错误类型
|
|
342
|
+
if (ErrorCodes.isSucceeded(errorInfo.errorCode)) {
|
|
343
|
+
console.log('操作成功');
|
|
344
|
+
} else {
|
|
345
|
+
console.error('操作失败:', errorInfo.errorMsg);
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### 函数类型
|
|
350
|
+
|
|
351
|
+
事件和命令处理程序的函数信息:
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import { FunctionInfo, FunctionKind } from '@ahoo-wang/fetcher-wow';
|
|
355
|
+
|
|
356
|
+
const functionInfo: FunctionInfo = {
|
|
357
|
+
functionKind: FunctionKind.COMMAND,
|
|
358
|
+
contextName: 'user-context',
|
|
359
|
+
processorName: 'UserProcessor',
|
|
360
|
+
name: 'CreateUser',
|
|
361
|
+
};
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## 🛠️ 高级用法
|
|
365
|
+
|
|
366
|
+
### 完整命令流程示例
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import {
|
|
370
|
+
CommandRequest,
|
|
371
|
+
CommandHeaders,
|
|
372
|
+
CommandResult,
|
|
373
|
+
CommandStage,
|
|
374
|
+
toCommandResultEventStream,
|
|
375
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
376
|
+
import { fetchEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
377
|
+
|
|
378
|
+
// 1. 创建命令请求
|
|
379
|
+
const commandRequest: CommandRequest = {
|
|
380
|
+
path: '/commands/user/CreateUser',
|
|
381
|
+
method: 'POST',
|
|
382
|
+
headers: {
|
|
383
|
+
[CommandHeaders.TENANT_ID]: 'tenant-123',
|
|
384
|
+
[CommandHeaders.REQUEST_ID]: 'req-' + Date.now(),
|
|
385
|
+
},
|
|
386
|
+
body: {
|
|
387
|
+
name: 'John Doe',
|
|
388
|
+
email: 'john@example.com',
|
|
389
|
+
},
|
|
390
|
+
timeout: 10000,
|
|
391
|
+
localFirst: true,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// 2. 执行命令并等待结果
|
|
395
|
+
async function executeCommand(request: CommandRequest): Promise<CommandResult> {
|
|
396
|
+
// 实现依赖于您的 HTTP 客户端
|
|
397
|
+
// 这只是一个示例结构
|
|
398
|
+
const response = await fetch('/api' + request.path, {
|
|
399
|
+
method: request.method,
|
|
400
|
+
headers: request.headers,
|
|
401
|
+
body: JSON.stringify(request.body),
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return response.json();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 3. 实时流式处理命令结果
|
|
408
|
+
async function streamCommandResults() {
|
|
409
|
+
const eventStream = fetchEventStream('/commands/stream');
|
|
410
|
+
const commandResultStream = toCommandResultEventStream(eventStream);
|
|
411
|
+
|
|
412
|
+
const reader = commandResultStream.getReader();
|
|
413
|
+
try {
|
|
414
|
+
while (true) {
|
|
415
|
+
const { done, value } = await reader.read();
|
|
416
|
+
if (done) break;
|
|
417
|
+
|
|
418
|
+
const result: CommandResult = value.data;
|
|
419
|
+
|
|
420
|
+
// 处理不同阶段
|
|
421
|
+
switch (result.stage) {
|
|
422
|
+
case CommandStage.SENT:
|
|
423
|
+
console.log('命令已发送到总线');
|
|
424
|
+
break;
|
|
425
|
+
case CommandStage.PROCESSED:
|
|
426
|
+
console.log('命令已被聚合根处理');
|
|
427
|
+
break;
|
|
428
|
+
case CommandStage.SNAPSHOT:
|
|
429
|
+
console.log('已生成快照');
|
|
430
|
+
break;
|
|
431
|
+
case CommandStage.PROJECTED:
|
|
432
|
+
console.log('事件已投影到读模型');
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} finally {
|
|
437
|
+
reader.releaseLock();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### 复杂查询构建
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import {
|
|
446
|
+
and,
|
|
447
|
+
or,
|
|
448
|
+
eq,
|
|
449
|
+
ne,
|
|
450
|
+
gt,
|
|
451
|
+
lt,
|
|
452
|
+
contains,
|
|
453
|
+
isIn,
|
|
454
|
+
notIn,
|
|
455
|
+
between,
|
|
456
|
+
startsWith,
|
|
457
|
+
endsWith,
|
|
458
|
+
elemMatch,
|
|
459
|
+
isNull,
|
|
460
|
+
notNull,
|
|
461
|
+
exists,
|
|
462
|
+
today,
|
|
463
|
+
thisWeek,
|
|
464
|
+
recentDays,
|
|
465
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
466
|
+
|
|
467
|
+
// 构建用户搜索的复杂查询
|
|
468
|
+
const userSearchQuery = {
|
|
469
|
+
condition: and(
|
|
470
|
+
eq('tenantId', 'tenant-123'),
|
|
471
|
+
ne('status', 'deleted'),
|
|
472
|
+
or(
|
|
473
|
+
// 按姓名或邮箱搜索
|
|
474
|
+
contains('name', 'john'),
|
|
475
|
+
contains('email', 'john'),
|
|
476
|
+
),
|
|
477
|
+
// 年龄和分数过滤
|
|
478
|
+
gt('age', 18),
|
|
479
|
+
between('score', 50, 100),
|
|
480
|
+
|
|
481
|
+
// 部门过滤
|
|
482
|
+
isIn('departments', 'engineering', 'marketing'),
|
|
483
|
+
notIn('blockedDepartments', 'hr', 'finance'),
|
|
484
|
+
|
|
485
|
+
// 字符串模式匹配
|
|
486
|
+
startsWith('employeeId', 'EMP-'),
|
|
487
|
+
endsWith('domain', '.com'),
|
|
488
|
+
|
|
489
|
+
// 数组匹配
|
|
490
|
+
elemMatch('roles', eq('name', 'admin')),
|
|
491
|
+
|
|
492
|
+
// 日期过滤
|
|
493
|
+
recentDays('lastLogin', 30),
|
|
494
|
+
thisWeek('createdAt'),
|
|
495
|
+
|
|
496
|
+
// 存在性检查
|
|
497
|
+
exists('phoneNumber'),
|
|
498
|
+
notNull('address'),
|
|
499
|
+
),
|
|
500
|
+
|
|
501
|
+
sort: [
|
|
502
|
+
{ field: 'score', direction: 'DESC' },
|
|
503
|
+
{ field: 'lastLogin', direction: 'DESC' },
|
|
504
|
+
],
|
|
505
|
+
|
|
506
|
+
projection: {
|
|
507
|
+
include: ['id', 'name', 'email', 'score', 'lastLogin', 'departments'],
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
pagination: {
|
|
511
|
+
index: 1,
|
|
512
|
+
size: 50,
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## 🧪 测试
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
# 运行测试
|
|
521
|
+
pnpm test
|
|
522
|
+
|
|
523
|
+
# 运行带覆盖率的测试
|
|
524
|
+
pnpm test --coverage
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## 🤝 贡献
|
|
528
|
+
|
|
529
|
+
欢迎贡献!请查看
|
|
530
|
+
[贡献指南](https://github.com/Ahoo-Wang/fetcher/blob/main/CONTRIBUTING.md) 获取更多详情。
|
|
531
|
+
|
|
532
|
+
## 📄 许可证
|
|
533
|
+
|
|
534
|
+
Apache-2.0
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
<p align="center">
|
|
539
|
+
<a href="https://github.com/Ahoo-Wang/fetcher">Fetcher</a> 生态系统的一部分
|
|
540
|
+
</p>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NullableAggregateVersionCapable } from './types';
|
|
2
|
+
import { HttpMethod, RequestHeaders } from '@ahoo-wang/fetcher';
|
|
3
|
+
export interface CommandRequest extends NullableAggregateVersionCapable {
|
|
4
|
+
path: string;
|
|
5
|
+
pathParams?: Record<string, any>;
|
|
6
|
+
method: HttpMethod;
|
|
7
|
+
headers: RequestHeaders;
|
|
8
|
+
body: Record<string, any>;
|
|
9
|
+
/**
|
|
10
|
+
* Command timeout period. Milliseconds
|
|
11
|
+
*/
|
|
12
|
+
timeout?: number;
|
|
13
|
+
aggregateId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The version of the target aggregate, which is used to control version conflicts
|
|
16
|
+
*/
|
|
17
|
+
aggregateVersion?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The request ID of the command message, which is used to check the idempotency of the command message
|
|
20
|
+
*/
|
|
21
|
+
requestId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to enable local priority mode, if false, it will be turned off, and the default is true.
|
|
24
|
+
*/
|
|
25
|
+
localFirst?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to enable event stream mode, if true, it will be turned on, and the default is false.
|
|
28
|
+
*/
|
|
29
|
+
stream?: boolean;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=commandRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandRequest.d.ts","sourceRoot":"","sources":["../../src/command/commandRequest.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,+BAA+B,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,WAAW,cAAe,SAAQ,+BAA+B;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AggregateId, AggregateNameCapable, ErrorInfo, FunctionInfoCapable, Identifier, NamedBoundedContext } from '../types';
|
|
2
|
+
import { CommandId, CommandResultCapable, CommandStageCapable, NullableAggregateVersionCapable, RequestId, SignalTimeCapable, WaitCommandIdCapable } from './types';
|
|
3
|
+
export interface CommandResult extends Identifier, WaitCommandIdCapable, CommandStageCapable, NamedBoundedContext, AggregateNameCapable, AggregateId, ErrorInfo, CommandId, RequestId, ErrorInfo, FunctionInfoCapable, CommandResultCapable, SignalTimeCapable, NullableAggregateVersionCapable {
|
|
4
|
+
/**
|
|
5
|
+
* 聚合根版本号
|
|
6
|
+
* - 命令处理成功时,为聚合根完成命令处理后的版本号
|
|
7
|
+
* - 当命令在命令网关验证失败时,为 null
|
|
8
|
+
* - 当命令在命令处理器中执行失败时,为聚合根的当前版本号
|
|
9
|
+
*/
|
|
10
|
+
aggregateVersion?: number;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=commandResult.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandResult.d.ts","sourceRoot":"","sources":["../../src/command/commandResult.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,mBAAmB,EACnB,UAAU,EACV,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,mBAAmB,EACnB,+BAA+B,EAC/B,SAAS,EACT,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,aACf,SAAQ,UAAU,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAChF,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,SAAS,EACT,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,+BAA+B;IACrH;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAE3B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ServerSentEvent, ServerSentEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
2
|
+
import { CommandResult } from './commandResult';
|
|
3
|
+
export interface CommandResultEvent extends Omit<ServerSentEvent, 'data'> {
|
|
4
|
+
data: CommandResult;
|
|
5
|
+
}
|
|
6
|
+
export declare class CommandResultEventTransform implements Transformer<ServerSentEvent, CommandResultEvent> {
|
|
7
|
+
transform(chunk: ServerSentEvent, controller: TransformStreamDefaultController<CommandResultEvent>): void;
|
|
8
|
+
}
|
|
9
|
+
export declare class CommandResultEventTransformStream extends TransformStream<ServerSentEvent, CommandResultEvent> {
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
12
|
+
export type CommandResultEventStream = ReadableStream<CommandResultEvent>;
|
|
13
|
+
export declare function toCommandResultEventStream(serverSentEventStream: ServerSentEventStream): CommandResultEventStream;
|
|
14
|
+
//# sourceMappingURL=commandResultEventStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandResultEventStream.d.ts","sourceRoot":"","sources":["../../src/command/commandResultEventStream.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC;IACvE,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,qBAAa,2BAA4B,YAAW,WAAW,CAAC,eAAe,EAAE,kBAAkB,CAAC;IAClG,SAAS,CACP,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,gCAAgC,CAAC,kBAAkB,CAAC;CASnE;AAED,qBAAa,iCAAkC,SAAQ,eAAe,CAAC,eAAe,EAAE,kBAAkB,CAAC;;CAI1G;AAED,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,kBAAkB,CAAC,CAAC;AAE1E,wBAAgB,0BAA0B,CACxC,qBAAqB,EAAE,qBAAqB,GAC3C,wBAAwB,CAE1B"}
|
package/dist/command/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAaA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAaA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,SAAS,CAAC"}
|
package/dist/command/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AggregateId, AggregateNameCapable, ErrorInfo, FunctionInfoCapable, Identifier, NamedBoundedContext } from '../types';
|
|
2
1
|
export interface CommandId {
|
|
3
2
|
commandId: string;
|
|
4
3
|
}
|
|
@@ -43,7 +42,10 @@ export interface CommandResultCapable {
|
|
|
43
42
|
export interface SignalTimeCapable {
|
|
44
43
|
signalTime: number;
|
|
45
44
|
}
|
|
46
|
-
export interface
|
|
45
|
+
export interface NullableAggregateVersionCapable {
|
|
46
|
+
/**
|
|
47
|
+
* The aggregate version of the aggregate.
|
|
48
|
+
*/
|
|
47
49
|
aggregateVersion?: number;
|
|
48
50
|
}
|
|
49
51
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/command/types.ts"],"names":[],"mappings":"AAaA,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/command/types.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,oBAAY,YAAY;IACtB;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,QAAQ,aAAa;IACrB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,aAAa,kBAAkB;IAE/B;;OAEG;IACH,YAAY,iBAAiB;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
|