@ahoo-wang/fetcher-wow 1.1.0 → 1.2.1
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 +275 -357
- package/README.zh-CN.md +283 -279
- package/dist/command/commandClient.d.ts +146 -0
- package/dist/command/commandClient.d.ts.map +1 -0
- package/dist/command/{commandHeaders.d.ts → commandHttpHeaders.d.ts} +6 -7
- package/dist/command/commandHttpHeaders.d.ts.map +1 -0
- package/dist/command/commandRequest.d.ts +26 -62
- package/dist/command/commandRequest.d.ts.map +1 -1
- package/dist/command/index.d.ts +2 -3
- package/dist/command/index.d.ts.map +1 -1
- package/dist/index.es.js +467 -226
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/query/event/domainEventStream.d.ts +49 -4
- package/dist/query/event/domainEventStream.d.ts.map +1 -1
- package/dist/query/event/eventStreamQueryApi.d.ts +6 -0
- package/dist/query/event/eventStreamQueryApi.d.ts.map +1 -1
- package/dist/query/event/eventStreamQueryClient.d.ts +46 -0
- package/dist/query/event/eventStreamQueryClient.d.ts.map +1 -0
- package/dist/query/event/index.d.ts +1 -0
- package/dist/query/event/index.d.ts.map +1 -1
- package/dist/query/queryApi.d.ts +62 -0
- package/dist/query/queryApi.d.ts.map +1 -1
- package/dist/query/snapshot/index.d.ts +1 -0
- package/dist/query/snapshot/index.d.ts.map +1 -1
- package/dist/query/snapshot/snapshot.d.ts +2 -2
- package/dist/query/snapshot/snapshot.d.ts.map +1 -1
- package/dist/query/snapshot/snapshotQueryApi.d.ts +28 -0
- package/dist/query/snapshot/snapshotQueryApi.d.ts.map +1 -1
- package/dist/query/snapshot/snapshotQueryClient.d.ts +74 -0
- package/dist/query/snapshot/snapshotQueryClient.d.ts.map +1 -0
- package/dist/query/sort.d.ts +4 -0
- package/dist/query/sort.d.ts.map +1 -1
- package/dist/types/client.d.ts +15 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/endpoints.d.ts +28 -0
- package/dist/types/endpoints.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/command/commandHeaders.d.ts.map +0 -1
- package/dist/command/commandHttpClient.d.ts +0 -133
- package/dist/command/commandHttpClient.d.ts.map +0 -1
- package/dist/command/commandHttpRequest.d.ts +0 -48
- package/dist/command/commandHttpRequest.d.ts.map +0 -1
package/README.zh-CN.md
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
## 🌟 特性
|
|
14
14
|
|
|
15
|
-
- **📦
|
|
16
|
-
-
|
|
17
|
-
- **🔍
|
|
18
|
-
- **📡
|
|
19
|
-
- **🔄 CQRS
|
|
20
|
-
- **🧱 DDD
|
|
15
|
+
- **📦 完整的 TypeScript 支持**:为所有 Wow 框架实体提供完整的类型定义,包括命令、事件和查询
|
|
16
|
+
- **🚀 命令客户端**:用于向 Wow 服务发送命令的高级客户端,支持同步和流式响应
|
|
17
|
+
- **🔍 强大的查询 DSL**:丰富的查询条件构建器,支持全面的操作符用于复杂查询
|
|
18
|
+
- **📡 实时事件流**:内置对服务器发送事件的支持,用于接收实时命令结果和数据更新
|
|
19
|
+
- **🔄 CQRS 模式实现**:对命令查询责任分离架构模式的一流支持
|
|
20
|
+
- **🧱 DDD 基础构件**:基本的领域驱动设计构建块,包括聚合、事件和值对象
|
|
21
|
+
- **🔍 查询客户端**:专门用于查询快照和事件流数据的客户端,支持全面的查询操作
|
|
21
22
|
|
|
22
23
|
## 🚀 快速开始
|
|
23
24
|
|
|
@@ -38,100 +39,87 @@ yarn add @ahoo-wang/fetcher-wow
|
|
|
38
39
|
|
|
39
40
|
### 命令模块
|
|
40
41
|
|
|
41
|
-
####
|
|
42
|
+
#### CommandResult
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
表示命令执行结果的接口:
|
|
44
45
|
|
|
45
46
|
```typescript
|
|
46
|
-
import {
|
|
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
|
-
};
|
|
47
|
+
import { CommandResult, CommandStage } from '@ahoo-wang/fetcher-wow';
|
|
58
48
|
```
|
|
59
49
|
|
|
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
|
|
50
|
+
#### CommandClient
|
|
72
51
|
|
|
73
|
-
|
|
52
|
+
用于向 Wow 框架发送命令的 HTTP 客户端。该客户端提供了同步或流式接收命令结果的方法。
|
|
74
53
|
|
|
75
54
|
```typescript
|
|
76
|
-
import {
|
|
55
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
56
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
57
|
+
import {
|
|
58
|
+
CommandClient,
|
|
59
|
+
CommandRequest,
|
|
60
|
+
HttpMethod,
|
|
61
|
+
CommandHttpHeaders,
|
|
62
|
+
CommandStage,
|
|
63
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
64
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
65
|
+
|
|
66
|
+
// 创建 fetcher 实例
|
|
67
|
+
const wowFetcher = new Fetcher({
|
|
68
|
+
baseURL: 'http://localhost:8080/',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 添加拦截器处理 URL 参数
|
|
72
|
+
const ownerId = idGenerator.generateId();
|
|
73
|
+
wowFetcher.interceptors.request.use({
|
|
74
|
+
name: 'AppendOwnerId',
|
|
75
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
76
|
+
intercept(exchange) {
|
|
77
|
+
exchange.request.urlParams = {
|
|
78
|
+
path: {
|
|
79
|
+
...exchange.request.urlParams?.path,
|
|
80
|
+
ownerId,
|
|
81
|
+
},
|
|
82
|
+
query: exchange.request.urlParams?.query,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 创建命令客户端
|
|
88
|
+
const commandClient = new CommandClient({
|
|
89
|
+
fetcher: wowFetcher,
|
|
90
|
+
basePath: 'owner/{ownerId}/cart',
|
|
91
|
+
});
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
method:
|
|
93
|
+
// 定义命令请求
|
|
94
|
+
const command: CommandRequest = {
|
|
95
|
+
method: HttpMethod.POST,
|
|
81
96
|
headers: {
|
|
82
|
-
[
|
|
97
|
+
[CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
|
|
83
98
|
},
|
|
84
99
|
body: {
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
productId: 'productId',
|
|
101
|
+
quantity: 1,
|
|
87
102
|
},
|
|
88
|
-
timeout: 5000,
|
|
89
|
-
aggregateId: 'user-456',
|
|
90
|
-
requestId: 'req-789',
|
|
91
|
-
localFirst: true,
|
|
92
|
-
stream: false,
|
|
93
103
|
};
|
|
94
|
-
```
|
|
95
104
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
表示命令执行结果的接口:
|
|
105
|
+
// 发送命令并等待结果
|
|
106
|
+
const commandResult = await commandClient.send('add_cart_item', command);
|
|
99
107
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
};
|
|
108
|
+
// 发送命令并接收流式结果
|
|
109
|
+
const commandResultStream = await commandClient.sendAndWaitStream(
|
|
110
|
+
'add_cart_item',
|
|
111
|
+
command,
|
|
112
|
+
);
|
|
113
|
+
for await (const commandResultEvent of commandResultStream) {
|
|
114
|
+
console.log('收到命令结果:', commandResultEvent.data);
|
|
115
|
+
}
|
|
122
116
|
```
|
|
123
117
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
命令结果事件流的类型别名:
|
|
118
|
+
##### 方法
|
|
127
119
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// CommandResultEventStream 是 CommandResult 的 JsonServerSentEventStream
|
|
133
|
-
type CommandResultEventStream = JsonServerSentEventStream<CommandResult>;
|
|
134
|
-
```
|
|
120
|
+
- `send(path: string, commandRequest: CommandRequest): Promise<CommandResult>` - 发送命令并等待结果。
|
|
121
|
+
- `sendAndWaitStream(path: string, commandRequest: CommandRequest): Promise<CommandResultEventStream>` -
|
|
122
|
+
发送命令并以服务器发送事件的形式返回结果流。
|
|
135
123
|
|
|
136
124
|
### 查询模块
|
|
137
125
|
|
|
@@ -183,245 +171,261 @@ const dateConditions = [
|
|
|
183
171
|
];
|
|
184
172
|
```
|
|
185
173
|
|
|
186
|
-
####
|
|
187
|
-
|
|
188
|
-
用于查询构建的完整操作符枚举:
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
import { Operator } from '@ahoo-wang/fetcher-wow';
|
|
192
|
-
|
|
193
|
-
// 逻辑操作符
|
|
194
|
-
(Operator.AND, Operator.OR, Operator.NOR);
|
|
195
|
-
|
|
196
|
-
// 比较操作符
|
|
197
|
-
(Operator.EQ,
|
|
198
|
-
Operator.NE,
|
|
199
|
-
Operator.GT,
|
|
200
|
-
Operator.LT,
|
|
201
|
-
Operator.GTE,
|
|
202
|
-
Operator.LTE);
|
|
203
|
-
|
|
204
|
-
// 成员操作符
|
|
205
|
-
(Operator.IN, Operator.NOT_IN, Operator.ALL_IN, Operator.BETWEEN);
|
|
206
|
-
|
|
207
|
-
// 字符串操作符
|
|
208
|
-
(Operator.CONTAINS, Operator.STARTS_WITH, Operator.ENDS_WITH);
|
|
209
|
-
|
|
210
|
-
// 存在性操作符
|
|
211
|
-
(Operator.NULL, Operator.NOT_NULL, Operator.EXISTS);
|
|
212
|
-
|
|
213
|
-
// 布尔操作符
|
|
214
|
-
(Operator.TRUE, Operator.FALSE);
|
|
215
|
-
|
|
216
|
-
// 日期操作符
|
|
217
|
-
(Operator.TODAY,
|
|
218
|
-
Operator.BEFORE_TODAY,
|
|
219
|
-
Operator.TOMORROW,
|
|
220
|
-
Operator.THIS_WEEK,
|
|
221
|
-
Operator.NEXT_WEEK,
|
|
222
|
-
Operator.LAST_WEEK,
|
|
223
|
-
Operator.THIS_MONTH,
|
|
224
|
-
Operator.LAST_MONTH,
|
|
225
|
-
Operator.RECENT_DAYS,
|
|
226
|
-
Operator.EARLIER_DAYS);
|
|
227
|
-
|
|
228
|
-
// 特殊操作符
|
|
229
|
-
(Operator.ID,
|
|
230
|
-
Operator.IDS,
|
|
231
|
-
Operator.AGGREGATE_ID,
|
|
232
|
-
Operator.AGGREGATE_IDS,
|
|
233
|
-
Operator.TENANT_ID,
|
|
234
|
-
Operator.OWNER_ID,
|
|
235
|
-
Operator.DELETED,
|
|
236
|
-
Operator.ALL,
|
|
237
|
-
Operator.ELEM_MATCH,
|
|
238
|
-
Operator.RAW);
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
#### 可查询接口
|
|
174
|
+
#### SnapshotQueryClient
|
|
242
175
|
|
|
243
|
-
|
|
176
|
+
用于查询物化快照的客户端:
|
|
244
177
|
|
|
245
178
|
```typescript
|
|
179
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
180
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
246
181
|
import {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
182
|
+
SnapshotQueryClient,
|
|
183
|
+
all,
|
|
184
|
+
ListQuery,
|
|
185
|
+
PagedQuery,
|
|
186
|
+
SingleQuery,
|
|
250
187
|
} from '@ahoo-wang/fetcher-wow';
|
|
188
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
251
189
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
{ field: 'name', direction: SortDirection.ASC },
|
|
257
|
-
],
|
|
258
|
-
projection: {
|
|
259
|
-
include: ['id', 'name', 'email', 'status'],
|
|
260
|
-
exclude: ['password', 'internalNotes'],
|
|
261
|
-
},
|
|
262
|
-
};
|
|
190
|
+
interface CartItem {
|
|
191
|
+
productId: string;
|
|
192
|
+
quantity: number;
|
|
193
|
+
}
|
|
263
194
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
size: 20,
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
```
|
|
195
|
+
interface CartState {
|
|
196
|
+
id: string;
|
|
197
|
+
items: CartItem[];
|
|
198
|
+
}
|
|
272
199
|
|
|
273
|
-
|
|
200
|
+
// 创建 fetcher 实例
|
|
201
|
+
const wowFetcher = new Fetcher({
|
|
202
|
+
baseURL: 'http://localhost:8080/',
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// 添加拦截器处理 URL 参数
|
|
206
|
+
const ownerId = idGenerator.generateId();
|
|
207
|
+
wowFetcher.interceptors.request.use({
|
|
208
|
+
name: 'AppendOwnerId',
|
|
209
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
210
|
+
intercept(exchange) {
|
|
211
|
+
exchange.request.urlParams = {
|
|
212
|
+
path: {
|
|
213
|
+
...exchange.request.urlParams?.path,
|
|
214
|
+
ownerId,
|
|
215
|
+
},
|
|
216
|
+
query: exchange.request.urlParams?.query,
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
});
|
|
274
220
|
|
|
275
|
-
|
|
221
|
+
// 创建快照查询客户端
|
|
222
|
+
const snapshotQueryClient = new SnapshotQueryClient<CartState>({
|
|
223
|
+
fetcher: wowFetcher,
|
|
224
|
+
basePath: 'owner/{ownerId}/cart',
|
|
225
|
+
});
|
|
276
226
|
|
|
277
|
-
|
|
227
|
+
// 统计快照数量
|
|
228
|
+
const count = await snapshotQueryClient.count(all());
|
|
278
229
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
OwnerId,
|
|
285
|
-
NamedAggregate,
|
|
286
|
-
AggregateId,
|
|
287
|
-
StateCapable,
|
|
288
|
-
} from '@ahoo-wang/fetcher-wow';
|
|
230
|
+
// 列出快照
|
|
231
|
+
const listQuery: ListQuery = {
|
|
232
|
+
condition: all(),
|
|
233
|
+
};
|
|
234
|
+
const list = await snapshotQueryClient.list(listQuery);
|
|
289
235
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
NamedAggregate,
|
|
296
|
-
StateCapable<UserState> {
|
|
297
|
-
id: string;
|
|
298
|
-
version: number;
|
|
299
|
-
tenantId: string;
|
|
300
|
-
ownerId: string;
|
|
301
|
-
contextName: string;
|
|
302
|
-
aggregateName: string;
|
|
303
|
-
state: UserState;
|
|
236
|
+
// 以流的形式列出快照
|
|
237
|
+
const listStream = await snapshotQueryClient.listStream(listQuery);
|
|
238
|
+
for await (const event of listStream) {
|
|
239
|
+
const snapshot = event.data;
|
|
240
|
+
console.log('收到快照:', snapshot);
|
|
304
241
|
}
|
|
305
242
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
email: string;
|
|
309
|
-
status: 'active' | 'inactive';
|
|
310
|
-
createdAt: number;
|
|
311
|
-
}
|
|
312
|
-
```
|
|
243
|
+
// 列出快照状态
|
|
244
|
+
const stateList = await snapshotQueryClient.listState(listQuery);
|
|
313
245
|
|
|
314
|
-
|
|
246
|
+
// 以流的形式列出快照状态
|
|
247
|
+
const stateStream = await snapshotQueryClient.listStateStream(listQuery);
|
|
248
|
+
for await (const event of stateStream) {
|
|
249
|
+
const state = event.data;
|
|
250
|
+
console.log('收到状态:', state);
|
|
251
|
+
}
|
|
315
252
|
|
|
316
|
-
|
|
253
|
+
// 分页查询快照
|
|
254
|
+
const pagedQuery: PagedQuery = {
|
|
255
|
+
condition: all(),
|
|
256
|
+
};
|
|
257
|
+
const paged = await snapshotQueryClient.paged(pagedQuery);
|
|
317
258
|
|
|
318
|
-
|
|
319
|
-
|
|
259
|
+
// 分页查询快照状态
|
|
260
|
+
const pagedState = await snapshotQueryClient.pagedState(pagedQuery);
|
|
320
261
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
bindingErrors: [],
|
|
262
|
+
// 查询单个快照
|
|
263
|
+
const singleQuery: SingleQuery = {
|
|
264
|
+
condition: all(),
|
|
325
265
|
};
|
|
266
|
+
const single = await snapshotQueryClient.single(singleQuery);
|
|
326
267
|
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
console.log('操作成功');
|
|
330
|
-
} else {
|
|
331
|
-
console.error('操作失败:', errorInfo.errorMsg);
|
|
332
|
-
}
|
|
268
|
+
// 查询单个快照状态
|
|
269
|
+
const singleState = await snapshotQueryClient.singleState(singleQuery);
|
|
333
270
|
```
|
|
334
271
|
|
|
335
|
-
####
|
|
272
|
+
#### EventStreamQueryClient
|
|
336
273
|
|
|
337
|
-
|
|
274
|
+
用于查询领域事件流的客户端:
|
|
338
275
|
|
|
339
276
|
```typescript
|
|
340
|
-
import {
|
|
277
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
278
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
279
|
+
import {
|
|
280
|
+
EventStreamQueryClient,
|
|
281
|
+
all,
|
|
282
|
+
ListQuery,
|
|
283
|
+
PagedQuery,
|
|
284
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
285
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
286
|
+
|
|
287
|
+
// 创建 fetcher 实例
|
|
288
|
+
const wowFetcher = new Fetcher({
|
|
289
|
+
baseURL: 'http://localhost:8080/',
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// 添加拦截器处理 URL 参数
|
|
293
|
+
const ownerId = idGenerator.generateId();
|
|
294
|
+
wowFetcher.interceptors.request.use({
|
|
295
|
+
name: 'AppendOwnerId',
|
|
296
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
297
|
+
intercept(exchange) {
|
|
298
|
+
exchange.request.urlParams = {
|
|
299
|
+
path: {
|
|
300
|
+
...exchange.request.urlParams?.path,
|
|
301
|
+
ownerId,
|
|
302
|
+
},
|
|
303
|
+
query: exchange.request.urlParams?.query,
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// 创建事件流查询客户端
|
|
309
|
+
const eventStreamQueryClient = new EventStreamQueryClient({
|
|
310
|
+
fetcher: wowFetcher,
|
|
311
|
+
basePath: 'owner/{ownerId}/cart',
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// 统计事件流数量
|
|
315
|
+
const count = await eventStreamQueryClient.count(all());
|
|
341
316
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
processorName: 'UserProcessor',
|
|
346
|
-
name: 'CreateUser',
|
|
317
|
+
// 列出事件流
|
|
318
|
+
const listQuery: ListQuery = {
|
|
319
|
+
condition: all(),
|
|
347
320
|
};
|
|
321
|
+
const list = await eventStreamQueryClient.list(listQuery);
|
|
322
|
+
|
|
323
|
+
// 以流的形式列出事件流
|
|
324
|
+
const listStream = await eventStreamQueryClient.listStream(listQuery);
|
|
325
|
+
for await (const event of listStream) {
|
|
326
|
+
const domainEventStream = event.data;
|
|
327
|
+
console.log('收到事件流:', domainEventStream);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 分页查询事件流
|
|
331
|
+
const pagedQuery: PagedQuery = {
|
|
332
|
+
condition: all(),
|
|
333
|
+
};
|
|
334
|
+
const paged = await eventStreamQueryClient.paged(pagedQuery);
|
|
348
335
|
```
|
|
349
336
|
|
|
350
337
|
## 🛠️ 高级用法
|
|
351
338
|
|
|
352
|
-
###
|
|
339
|
+
### 完整的命令和查询流程示例
|
|
353
340
|
|
|
354
341
|
```typescript
|
|
342
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
343
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
355
344
|
import {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
notIn,
|
|
365
|
-
between,
|
|
366
|
-
startsWith,
|
|
367
|
-
endsWith,
|
|
368
|
-
elemMatch,
|
|
369
|
-
isNull,
|
|
370
|
-
notNull,
|
|
371
|
-
exists,
|
|
372
|
-
today,
|
|
373
|
-
thisWeek,
|
|
374
|
-
recentDays,
|
|
345
|
+
CommandClient,
|
|
346
|
+
CommandRequest,
|
|
347
|
+
CommandHttpHeaders,
|
|
348
|
+
CommandStage,
|
|
349
|
+
HttpMethod,
|
|
350
|
+
SnapshotQueryClient,
|
|
351
|
+
all,
|
|
352
|
+
ListQuery,
|
|
375
353
|
} from '@ahoo-wang/fetcher-wow';
|
|
354
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
376
355
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
ne('status', 'deleted'),
|
|
382
|
-
or(
|
|
383
|
-
// 按姓名或邮箱搜索
|
|
384
|
-
contains('name', 'john'),
|
|
385
|
-
contains('email', 'john'),
|
|
386
|
-
),
|
|
387
|
-
// 年龄和分数过滤
|
|
388
|
-
gt('age', 18),
|
|
389
|
-
between('score', 50, 100),
|
|
390
|
-
|
|
391
|
-
// 部门过滤
|
|
392
|
-
isIn('departments', 'engineering', 'marketing'),
|
|
393
|
-
notIn('blockedDepartments', 'hr', 'finance'),
|
|
394
|
-
|
|
395
|
-
// 字符串模式匹配
|
|
396
|
-
startsWith('employeeId', 'EMP-'),
|
|
397
|
-
endsWith('domain', '.com'),
|
|
398
|
-
|
|
399
|
-
// 数组匹配
|
|
400
|
-
elemMatch('roles', eq('name', 'admin')),
|
|
401
|
-
|
|
402
|
-
// 日期过滤
|
|
403
|
-
recentDays('lastLogin', 30),
|
|
404
|
-
thisWeek('createdAt'),
|
|
405
|
-
|
|
406
|
-
// 存在性检查
|
|
407
|
-
exists('phoneNumber'),
|
|
408
|
-
notNull('address'),
|
|
409
|
-
),
|
|
356
|
+
interface CartItem {
|
|
357
|
+
productId: string;
|
|
358
|
+
quantity: number;
|
|
359
|
+
}
|
|
410
360
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
361
|
+
interface CartState {
|
|
362
|
+
id: string;
|
|
363
|
+
items: CartItem[];
|
|
364
|
+
}
|
|
415
365
|
|
|
416
|
-
|
|
417
|
-
|
|
366
|
+
// 创建 fetcher 实例
|
|
367
|
+
const wowFetcher = new Fetcher({
|
|
368
|
+
baseURL: 'http://localhost:8080/',
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// 添加拦截器处理 URL 参数
|
|
372
|
+
const ownerId = idGenerator.generateId();
|
|
373
|
+
wowFetcher.interceptors.request.use({
|
|
374
|
+
name: 'AppendOwnerId',
|
|
375
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
376
|
+
intercept(exchange) {
|
|
377
|
+
exchange.request.urlParams = {
|
|
378
|
+
path: {
|
|
379
|
+
...exchange.request.urlParams?.path,
|
|
380
|
+
ownerId,
|
|
381
|
+
},
|
|
382
|
+
query: exchange.request.urlParams?.query,
|
|
383
|
+
};
|
|
418
384
|
},
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// 创建客户端
|
|
388
|
+
const commandClient = new CommandClient({
|
|
389
|
+
fetcher: wowFetcher,
|
|
390
|
+
basePath: 'owner/{ownerId}/cart',
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const snapshotQueryClient = new SnapshotQueryClient<CartState>({
|
|
394
|
+
fetcher: wowFetcher,
|
|
395
|
+
basePath: 'owner/{ownerId}/cart',
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// 1. 发送命令添加商品到购物车
|
|
399
|
+
const addItemCommand: CommandRequest = {
|
|
400
|
+
method: HttpMethod.POST,
|
|
401
|
+
headers: {
|
|
402
|
+
[CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
|
|
403
|
+
},
|
|
404
|
+
body: {
|
|
405
|
+
productId: 'product-123',
|
|
406
|
+
quantity: 2,
|
|
423
407
|
},
|
|
424
408
|
};
|
|
409
|
+
|
|
410
|
+
const commandResult = await commandClient.send('add_cart_item', addItemCommand);
|
|
411
|
+
console.log('命令执行完成:', commandResult);
|
|
412
|
+
|
|
413
|
+
// 2. 查询更新后的购物车
|
|
414
|
+
const listQuery: ListQuery = {
|
|
415
|
+
condition: all(),
|
|
416
|
+
};
|
|
417
|
+
const carts = await snapshotQueryClient.list(listQuery);
|
|
418
|
+
|
|
419
|
+
for (const cart of carts) {
|
|
420
|
+
console.log('购物车:', cart.state);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 3. 流式监听购物车更新
|
|
424
|
+
const listStream = await snapshotQueryClient.listStream(listQuery);
|
|
425
|
+
for await (const event of listStream) {
|
|
426
|
+
const cart = event.data;
|
|
427
|
+
console.log('购物车更新:', cart.state);
|
|
428
|
+
}
|
|
425
429
|
```
|
|
426
430
|
|
|
427
431
|
## 🧪 测试
|