@qlover/create-app 0.6.0 → 0.6.2
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/CHANGELOG.md +37 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/config/IOCIdentifier.ts +13 -0
- package/dist/templates/react-app/docs/zh/bootstrap.md +555 -0
- package/dist/templates/react-app/docs/zh/env.md +480 -0
- package/dist/templates/react-app/docs/zh/global.md +510 -0
- package/dist/templates/react-app/docs/zh/ioc.md +410 -0
- package/dist/templates/react-app/package.json +1 -1
- package/dist/templates/react-app/src/base/apis/AiApi.ts +10 -5
- package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +1 -1
- package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +10 -17
- package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +2 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +7 -5
- package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +3 -2
- package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +33 -0
- package/dist/templates/react-app/src/base/cases/RequestLogger.ts +1 -1
- package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +2 -2
- package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +1 -1
- package/dist/templates/react-app/src/base/services/RouteService.ts +5 -2
- package/dist/templates/react-app/src/base/services/UserService.ts +8 -10
- package/dist/templates/react-app/src/core/IOC.ts +73 -83
- package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +52 -4
- package/dist/templates/react-app/src/core/bootstraps/{index.ts → BootstrapsRegistry.ts} +2 -3
- package/dist/templates/react-app/src/core/registers/IocRegisterImpl.ts +25 -0
- package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +11 -17
- package/dist/templates/react-app/src/core/registers/RegisterControllers.ts +10 -4
- package/dist/templates/react-app/src/core/registers/RegisterGlobals.ts +6 -15
- package/dist/templates/react-app/src/main.tsx +2 -5
- package/dist/templates/react-app/src/uikit/controllers/JSONStorageController.ts +1 -1
- package/dist/templates/react-app/tsconfig.app.json +2 -1
- package/dist/templates/react-app/tsconfig.node.json +2 -1
- package/dist/templates/react-app/vite.config.ts +1 -1
- package/package.json +2 -2
- package/dist/templates/react-app/src/base/port/ApiTransactionInterface.ts +0 -7
- package/dist/templates/react-app/src/base/port/RequestCatcherInterface.ts +0 -12
- package/dist/templates/react-app/src/core/bootstrap.ts +0 -58
- package/dist/templates/react-app/src/core/registers/RegisterApi.ts +0 -5
- package/dist/templates/react-app/src/core/registers/index.ts +0 -32
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# IOC 容器
|
|
2
|
+
|
|
3
|
+
## 什么是 IOC?
|
|
4
|
+
|
|
5
|
+
IOC(Inversion of Control,控制反转)是一种设计模式,它将对象的创建和依赖关系的管理交给容器来处理,而不是在代码中直接创建对象。
|
|
6
|
+
|
|
7
|
+
**简单来说**:就像工厂生产产品一样,你不需要知道产品是如何制造的,只需要告诉工厂你需要什么产品,工厂就会给你提供。
|
|
8
|
+
|
|
9
|
+
## 项目中的 IOC 实现
|
|
10
|
+
|
|
11
|
+
### 核心技术栈
|
|
12
|
+
|
|
13
|
+
- **InversifyJS**:作为 IOC 容器的实现(你也可以手动实现自己的容器)
|
|
14
|
+
- **TypeScript**:提供类型安全
|
|
15
|
+
- **装饰器模式**:使用 `@injectable()` 和 `@inject()` 装饰器
|
|
16
|
+
|
|
17
|
+
### 核心文件结构
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/
|
|
21
|
+
├── core/
|
|
22
|
+
│ ├── IOC.ts # IOC 主入口
|
|
23
|
+
│ ├── registers/ # 注册器目录
|
|
24
|
+
│ │ ├── RegisterGlobals.ts # 全局服务注册
|
|
25
|
+
│ │ ├── RegisterCommon.ts # 通用服务注册
|
|
26
|
+
│ │ └── RegisterControllers.ts # 控制器注册
|
|
27
|
+
│ └── globals.ts # 全局实例
|
|
28
|
+
├── base/
|
|
29
|
+
│ └── cases/
|
|
30
|
+
│ └── InversifyContainer.ts # Inversify 容器实现
|
|
31
|
+
└── config/
|
|
32
|
+
└── IOCIdentifier.ts # IOC 标识符定义
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 基本概念
|
|
36
|
+
|
|
37
|
+
### 1. IOC 标识符
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// config/IOCIdentifier.ts
|
|
41
|
+
export const IOCIdentifier = Object.freeze({
|
|
42
|
+
JSON: 'JSON',
|
|
43
|
+
LocalStorage: 'LocalStorage',
|
|
44
|
+
Logger: 'Logger',
|
|
45
|
+
AppConfig: 'AppConfig',
|
|
46
|
+
// ... 更多标识符
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. IOC 标识符映射
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// core/IOC.ts
|
|
54
|
+
export interface IOCIdentifierMap {
|
|
55
|
+
[IOCIdentifier.JSON]: import('@qlover/fe-corekit').JSONSerializer;
|
|
56
|
+
[IOCIdentifier.LocalStorage]: import('@qlover/fe-corekit').ObjectStorage<string, string>;
|
|
57
|
+
[IOCIdentifier.Logger]: import('@qlover/logger').LoggerInterface;
|
|
58
|
+
[IOCIdentifier.AppConfig]: import('@qlover/corekit-bridge').EnvConfigInterface;
|
|
59
|
+
// ... 更多映射
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. IOC 函数
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
// core/IOC.ts
|
|
67
|
+
export const IOC = createIOCFunction<IOCIdentifierMap>(
|
|
68
|
+
new InversifyContainer()
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 使用方法
|
|
73
|
+
|
|
74
|
+
### 1. 获取服务实例
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
// 使用类名获取
|
|
78
|
+
const userService = IOC(UserService);
|
|
79
|
+
|
|
80
|
+
// 使用字符串标识符获取
|
|
81
|
+
const logger = IOC('Logger');
|
|
82
|
+
// 或者
|
|
83
|
+
const logger = IOC(IOCIdentifier.Logger);
|
|
84
|
+
|
|
85
|
+
// 使用 AppConfig
|
|
86
|
+
const appConfig = IOC(IOCIdentifier.AppConfig);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. 在服务中使用依赖注入
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { inject, injectable } from 'inversify';
|
|
93
|
+
import { UserApi } from '@/base/apis/userApi/UserApi';
|
|
94
|
+
import { RouteService } from './RouteService';
|
|
95
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
96
|
+
|
|
97
|
+
@injectable()
|
|
98
|
+
export class UserService extends UserAuthService<UserInfo> {
|
|
99
|
+
constructor(
|
|
100
|
+
@inject(RouteService) protected routerService: RouteService,
|
|
101
|
+
@inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
|
|
102
|
+
@inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
|
|
103
|
+
@inject(IOCIdentifier.LocalStorageEncrypt) storage: SyncStorageInterface<string, string>
|
|
104
|
+
) {
|
|
105
|
+
super(userApi, {
|
|
106
|
+
userStorage: {
|
|
107
|
+
key: appConfig.userInfoStorageKey,
|
|
108
|
+
storage: storage
|
|
109
|
+
},
|
|
110
|
+
credentialStorage: {
|
|
111
|
+
key: appConfig.userTokenStorageKey,
|
|
112
|
+
storage: storage
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. 在 Bootstrap 中使用
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
// 在启动器中注册服务
|
|
123
|
+
bootstrap.use([
|
|
124
|
+
IOC(UserService), // 用户服务
|
|
125
|
+
IOC(I18nService), // 国际化服务
|
|
126
|
+
new UserApiBootstarp() // API 配置
|
|
127
|
+
]);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 服务注册
|
|
131
|
+
|
|
132
|
+
### 1. 全局服务注册
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
// core/registers/RegisterGlobals.ts
|
|
136
|
+
export const RegisterGlobals: IOCRegister = {
|
|
137
|
+
register(container, _, options): void {
|
|
138
|
+
// 注册应用配置
|
|
139
|
+
container.bind(IOCIdentifier.AppConfig, options!.appConfig);
|
|
140
|
+
|
|
141
|
+
// 注册日志服务
|
|
142
|
+
container.bind(Logger, logger);
|
|
143
|
+
container.bind(IOCIdentifier.Logger, logger);
|
|
144
|
+
|
|
145
|
+
// 注册存储服务
|
|
146
|
+
container.bind(IOCIdentifier.LocalStorage, localStorage);
|
|
147
|
+
container.bind(IOCIdentifier.LocalStorageEncrypt, localStorageEncrypt);
|
|
148
|
+
container.bind(IOCIdentifier.CookieStorage, cookieStorage);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 2. 通用服务注册
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
// core/registers/RegisterCommon.ts
|
|
157
|
+
export const RegisterCommon: IOCRegister = {
|
|
158
|
+
register(container, _, options): void {
|
|
159
|
+
const AppConfig = container.get(IOCIdentifier.AppConfig);
|
|
160
|
+
|
|
161
|
+
// 注册 API 相关服务
|
|
162
|
+
const feApiToken = new TokenStorage(AppConfig.userTokenStorageKey, {
|
|
163
|
+
storage: container.get(IOCIdentifier.LocalStorageEncrypt)
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
container.bind(IOCIdentifier.FeApiToken, feApiToken);
|
|
167
|
+
container.bind(IOCIdentifier.FeApiCommonPlugin, feApiRequestCommonPlugin);
|
|
168
|
+
|
|
169
|
+
// 注册主题服务
|
|
170
|
+
container.bind(ThemeService, new ThemeService({
|
|
171
|
+
...themeConfig,
|
|
172
|
+
storage: localStorage
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// 注册路由服务
|
|
176
|
+
container.bind(RouteService, new RouteService({
|
|
177
|
+
routes: baseRoutes,
|
|
178
|
+
logger
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
// 注册国际化服务
|
|
182
|
+
container.bind(I18nService, new I18nService(options!.pathname));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 3. 控制器注册
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
// core/registers/RegisterControllers.ts
|
|
191
|
+
export class RegisterControllers implements IOCRegister {
|
|
192
|
+
register(container: IOCContainer, _: IOCManagerInterface<IOCContainer>): void {
|
|
193
|
+
// 注册控制器
|
|
194
|
+
const jsonStorageController = new JSONStorageController(localStorage);
|
|
195
|
+
container.bind(JSONStorageController, jsonStorageController);
|
|
196
|
+
|
|
197
|
+
// 配置处理器
|
|
198
|
+
container
|
|
199
|
+
.get(ProcesserExecutor)
|
|
200
|
+
.use(container.get(I18nKeyErrorPlugin))
|
|
201
|
+
.use(container.get(UserService));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 实际应用场景
|
|
207
|
+
|
|
208
|
+
### 1. 用户认证服务
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
@injectable()
|
|
212
|
+
export class UserService extends UserAuthService<UserInfo> {
|
|
213
|
+
constructor(
|
|
214
|
+
@inject(RouteService) protected routerService: RouteService,
|
|
215
|
+
@inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
|
|
216
|
+
@inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
|
|
217
|
+
@inject(IOCIdentifier.LocalStorageEncrypt) storage: SyncStorageInterface<string, string>
|
|
218
|
+
) {
|
|
219
|
+
super(userApi, {
|
|
220
|
+
userStorage: {
|
|
221
|
+
key: appConfig.userInfoStorageKey,
|
|
222
|
+
storage: storage
|
|
223
|
+
},
|
|
224
|
+
credentialStorage: {
|
|
225
|
+
key: appConfig.userTokenStorageKey,
|
|
226
|
+
storage: storage
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async onBefore(): Promise<void> {
|
|
232
|
+
if (this.isAuthenticated()) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const userToken = this.getToken();
|
|
237
|
+
if (!userToken) {
|
|
238
|
+
throw new AppError('NO_USER_TOKEN');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await this.userInfo();
|
|
242
|
+
this.store.authSuccess();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 2. API 配置服务
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
export class UserApiBootstarp implements BootstrapExecutorPlugin {
|
|
251
|
+
readonly pluginName = 'UserApiBootstarp';
|
|
252
|
+
|
|
253
|
+
onBefore({ parameters: { ioc } }: BootstrapContext): void {
|
|
254
|
+
// 通过 IOC 获取 UserApi 实例并配置插件
|
|
255
|
+
ioc
|
|
256
|
+
.get<UserApi>(UserApi)
|
|
257
|
+
.usePlugin(new FetchURLPlugin())
|
|
258
|
+
.usePlugin(IOC.get(IOCIdentifier.ApiMockPlugin))
|
|
259
|
+
.usePlugin(IOC.get(RequestLogger));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 3. 在组件中使用
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
// 在 React 组件中使用 IOC 服务
|
|
268
|
+
function UserProfile() {
|
|
269
|
+
const userService = IOC(UserService);
|
|
270
|
+
const { user } = useStore(userService.store);
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<h1>欢迎, {user?.name}</h1>
|
|
275
|
+
<button onClick={() => userService.logout()}>退出登录</button>
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## 最佳实践
|
|
282
|
+
|
|
283
|
+
### 1. 服务设计原则
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
// ✅ 好的设计:单一职责
|
|
287
|
+
@injectable()
|
|
288
|
+
export class UserService {
|
|
289
|
+
constructor(
|
|
290
|
+
@inject(UserApi) private userApi: UserApi,
|
|
291
|
+
@inject(IOCIdentifier.AppConfig) private appConfig: AppConfig
|
|
292
|
+
) {}
|
|
293
|
+
|
|
294
|
+
async getUserInfo(): Promise<UserInfo> {
|
|
295
|
+
return this.userApi.getUserInfo();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ❌ 不好的设计:职责过多
|
|
300
|
+
@injectable()
|
|
301
|
+
export class BadService {
|
|
302
|
+
constructor(
|
|
303
|
+
@inject(UserApi) private userApi: UserApi,
|
|
304
|
+
@inject(RouteService) private routeService: RouteService,
|
|
305
|
+
@inject(ThemeService) private themeService: ThemeService,
|
|
306
|
+
@inject(I18nService) private i18nService: I18nService
|
|
307
|
+
) {}
|
|
308
|
+
|
|
309
|
+
// 一个服务做了太多事情
|
|
310
|
+
async handleUserAction(): Promise<void> {
|
|
311
|
+
// 处理用户逻辑
|
|
312
|
+
// 处理路由逻辑
|
|
313
|
+
// 处理主题逻辑
|
|
314
|
+
// 处理国际化逻辑
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 2. 依赖注入的最佳实践
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
// ✅ 使用接口而不是具体实现
|
|
323
|
+
@injectable()
|
|
324
|
+
export class UserService {
|
|
325
|
+
constructor(
|
|
326
|
+
@inject('UserApiInterface') private userApi: UserAuthApiInterface<UserInfo>
|
|
327
|
+
) {}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ✅ 使用标识符而不是类名
|
|
331
|
+
@injectable()
|
|
332
|
+
export class SomeService {
|
|
333
|
+
constructor(
|
|
334
|
+
@inject(IOCIdentifier.Logger) private logger: LoggerInterface,
|
|
335
|
+
@inject(IOCIdentifier.AppConfig) private appConfig: AppConfig
|
|
336
|
+
) {}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 3. 错误处理
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
@injectable()
|
|
344
|
+
export class SafeService {
|
|
345
|
+
constructor(
|
|
346
|
+
@inject(IOCIdentifier.Logger) private logger: LoggerInterface
|
|
347
|
+
) {}
|
|
348
|
+
|
|
349
|
+
async doSomething(): Promise<void> {
|
|
350
|
+
try {
|
|
351
|
+
// 业务逻辑
|
|
352
|
+
} catch (error) {
|
|
353
|
+
this.logger.error('操作失败:', error);
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## 调试和测试
|
|
361
|
+
|
|
362
|
+
### 1. 调试 IOC 容器
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
// 检查服务是否已注册
|
|
366
|
+
const container = IOC.implemention;
|
|
367
|
+
const isRegistered = container.isBound(UserService);
|
|
368
|
+
|
|
369
|
+
// 获取所有已注册的服务
|
|
370
|
+
const bindings = container.getAll(UserService);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 2. 单元测试
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
import { Container } from 'inversify';
|
|
377
|
+
|
|
378
|
+
describe('UserService', () => {
|
|
379
|
+
let container: Container;
|
|
380
|
+
let userService: UserService;
|
|
381
|
+
|
|
382
|
+
beforeEach(() => {
|
|
383
|
+
container = new Container();
|
|
384
|
+
|
|
385
|
+
// 注册测试依赖
|
|
386
|
+
container.bind('UserApiInterface').toConstantValue(mockUserApi);
|
|
387
|
+
container.bind(IOCIdentifier.AppConfig).toConstantValue(mockAppConfig);
|
|
388
|
+
container.bind(IOCIdentifier.LocalStorageEncrypt).toConstantValue(mockStorage);
|
|
389
|
+
|
|
390
|
+
userService = container.get(UserService);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('should authenticate user successfully', async () => {
|
|
394
|
+
const result = await userService.onBefore();
|
|
395
|
+
expect(result).toBeDefined();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## 总结
|
|
401
|
+
|
|
402
|
+
IOC 容器在项目中的作用:
|
|
403
|
+
|
|
404
|
+
1. **依赖管理**:统一管理所有服务的依赖关系
|
|
405
|
+
2. **类型安全**:通过 TypeScript 提供编译时类型检查
|
|
406
|
+
3. **可测试性**:便于进行单元测试和模拟
|
|
407
|
+
4. **可维护性**:清晰的依赖关系,易于理解和修改
|
|
408
|
+
5. **可扩展性**:轻松添加新的服务和依赖
|
|
409
|
+
|
|
410
|
+
通过合理使用 IOC 容器,可以让代码更加模块化、可测试和可维护。
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dev": "vite --mode localhost",
|
|
47
47
|
"dev:staging": "vite --mode staging",
|
|
48
48
|
"dev:prod": "vite --mode production",
|
|
49
|
-
"build": "vite build",
|
|
49
|
+
"build": "npm run lint && vite build",
|
|
50
50
|
"lint": "tsc -b --noEmit && eslint ./src --fix",
|
|
51
51
|
"prettier": "prettier --write ./src",
|
|
52
52
|
"preview": "vite preview",
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
RequestCommonPlugin
|
|
9
9
|
} from '@qlover/corekit-bridge';
|
|
10
10
|
import { RequestLogger } from '../cases/RequestLogger';
|
|
11
|
-
import { IOCIdentifier } from '
|
|
12
|
-
import {
|
|
11
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
12
|
+
import type { AppConfig } from '../cases/AppConfig';
|
|
13
13
|
|
|
14
14
|
const apiApiAdapter = new RequestAdapterFetch({
|
|
15
|
-
|
|
15
|
+
responseType: 'json'
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
// 使用 RequestScheduler
|
|
@@ -24,6 +24,11 @@ const apiApi = new RequestScheduler(apiApiAdapter);
|
|
|
24
24
|
export const AiApiBootstarp: BootstrapExecutorPlugin = {
|
|
25
25
|
pluginName: 'AiApiBootstarp',
|
|
26
26
|
onBefore({ parameters: { ioc } }) {
|
|
27
|
+
const appConfig = ioc.get<AppConfig>(IOCIdentifier.AppConfig);
|
|
28
|
+
|
|
29
|
+
// dynamic set baseURL
|
|
30
|
+
apiApiAdapter.config.baseURL = appConfig.aiApiBaseUrl;
|
|
31
|
+
|
|
27
32
|
apiApiAdapter.usePlugin(new FetchURLPlugin());
|
|
28
33
|
apiApiAdapter.usePlugin(
|
|
29
34
|
new RequestCommonPlugin({
|
|
@@ -40,13 +45,13 @@ export function aiHello(data: {
|
|
|
40
45
|
messages: { role: string; content: string }[];
|
|
41
46
|
}) {
|
|
42
47
|
return apiApi.request<
|
|
43
|
-
typeof data,
|
|
44
48
|
{
|
|
45
49
|
id: string;
|
|
46
50
|
object: string;
|
|
47
51
|
created: number;
|
|
48
52
|
choices: { message: { role: string; content: string } }[];
|
|
49
|
-
}
|
|
53
|
+
},
|
|
54
|
+
typeof data
|
|
50
55
|
>({
|
|
51
56
|
url: '/chat/completions',
|
|
52
57
|
method: 'POST',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
2
|
-
import { IOCIdentifier } from '
|
|
2
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
3
3
|
import { RequestAdapterFetch } from '@qlover/fe-corekit';
|
|
4
4
|
import { inject, injectable } from 'inversify';
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ApiPickDataPlugin
|
|
12
12
|
} from '@qlover/corekit-bridge';
|
|
13
13
|
import { FeApi } from './FeApi';
|
|
14
|
-
import { IOCIdentifier } from '
|
|
14
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
15
15
|
import { RequestLogger } from '@/base/cases/RequestLogger';
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -7,26 +7,21 @@ import {
|
|
|
7
7
|
GetIpInfoTransaction,
|
|
8
8
|
UserApiGetUserInfoTransaction,
|
|
9
9
|
UserApiLoginTransaction,
|
|
10
|
-
UserApiTestApiCatchResultTransaction
|
|
10
|
+
UserApiTestApiCatchResultTransaction,
|
|
11
|
+
UserInfo
|
|
11
12
|
} from './UserApiType';
|
|
12
13
|
import { inject, injectable } from 'inversify';
|
|
13
14
|
import { UserApiAdapter } from './UserApiAdapter';
|
|
14
15
|
import { UserApiConfig } from './UserApiBootstarp';
|
|
15
|
-
import {
|
|
16
|
+
import type {
|
|
16
17
|
LoginResponseData,
|
|
17
18
|
UserAuthApiInterface,
|
|
18
|
-
UserAuthStoreInterface
|
|
19
|
-
UserAuthState
|
|
19
|
+
UserAuthStoreInterface
|
|
20
20
|
} from '@qlover/corekit-bridge';
|
|
21
|
-
import {
|
|
22
|
-
RegisterFormData,
|
|
23
|
-
UserServiceUserInfo
|
|
24
|
-
} from '@/base/services/UserService';
|
|
21
|
+
import { RegisterFormData } from '@/base/services/UserService';
|
|
25
22
|
import { RES_NO_TOKEN } from '@config/Identifier';
|
|
26
23
|
import { AppError } from '@/base/cases/AppError';
|
|
27
24
|
|
|
28
|
-
export type UserApiState = UserAuthState<UserServiceUserInfo>;
|
|
29
|
-
|
|
30
25
|
/**
|
|
31
26
|
* UserApi
|
|
32
27
|
*
|
|
@@ -37,9 +32,9 @@ export type UserApiState = UserAuthState<UserServiceUserInfo>;
|
|
|
37
32
|
@injectable()
|
|
38
33
|
export class UserApi
|
|
39
34
|
extends RequestTransaction<UserApiConfig>
|
|
40
|
-
implements UserAuthApiInterface<
|
|
35
|
+
implements UserAuthApiInterface<UserInfo>
|
|
41
36
|
{
|
|
42
|
-
protected store: UserAuthStoreInterface<
|
|
37
|
+
protected store: UserAuthStoreInterface<UserInfo> | null = null;
|
|
43
38
|
|
|
44
39
|
constructor(
|
|
45
40
|
@inject(FetchAbortPlugin) private abortPlugin: FetchAbortPlugin,
|
|
@@ -48,7 +43,7 @@ export class UserApi
|
|
|
48
43
|
super(adapter);
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
getStore(): UserAuthStoreInterface<
|
|
46
|
+
getStore(): UserAuthStoreInterface<UserInfo> | null {
|
|
52
47
|
return this.store;
|
|
53
48
|
}
|
|
54
49
|
|
|
@@ -56,7 +51,7 @@ export class UserApi
|
|
|
56
51
|
* @override
|
|
57
52
|
* @param store
|
|
58
53
|
*/
|
|
59
|
-
setStore(store: UserAuthStoreInterface<
|
|
54
|
+
setStore(store: UserAuthStoreInterface<UserInfo>): void {
|
|
60
55
|
this.store = store;
|
|
61
56
|
}
|
|
62
57
|
|
|
@@ -130,9 +125,7 @@ export class UserApi
|
|
|
130
125
|
* @override
|
|
131
126
|
* @returns
|
|
132
127
|
*/
|
|
133
|
-
async getUserInfo(): Promise<
|
|
134
|
-
UserApiGetUserInfoTransaction['response']['data']
|
|
135
|
-
> {
|
|
128
|
+
async getUserInfo(): Promise<UserInfo> {
|
|
136
129
|
const response =
|
|
137
130
|
await this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
|
|
138
131
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
2
|
-
import { IOCIdentifier } from '
|
|
2
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
3
3
|
import { RequestAdapterFetch } from '@qlover/fe-corekit';
|
|
4
4
|
import { inject, injectable } from 'inversify';
|
|
5
5
|
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
RequestAdapterResponse,
|
|
5
5
|
RequestTransactionInterface
|
|
6
6
|
} from '@qlover/fe-corekit';
|
|
7
|
-
import { IOC
|
|
7
|
+
import { IOC } from '@/core/IOC';
|
|
8
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
8
9
|
import { RequestLogger } from '@/base/cases/RequestLogger';
|
|
9
10
|
import { FetchURLPlugin } from '@qlover/fe-corekit';
|
|
10
11
|
import {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { UserApiTransaction } from './UserApiBootstarp';
|
|
2
2
|
|
|
3
|
+
export type UserInfo = {
|
|
4
|
+
name: string;
|
|
5
|
+
email: string;
|
|
6
|
+
picture: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export type GetIpInfoTransaction = UserApiTransaction<
|
|
4
10
|
undefined,
|
|
5
11
|
{
|
|
@@ -35,11 +41,7 @@ export type UserApiGetRandomUser = UserApiTransaction<
|
|
|
35
41
|
|
|
36
42
|
export type UserApiGetUserInfoTransaction = UserApiTransaction<
|
|
37
43
|
string,
|
|
38
|
-
|
|
39
|
-
name: string;
|
|
40
|
-
email: string;
|
|
41
|
-
picture: string;
|
|
42
|
-
}
|
|
44
|
+
UserInfo
|
|
43
45
|
>;
|
|
44
46
|
|
|
45
47
|
export type UserApiLoginTransaction = UserApiTransaction<
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { ExecutorContext, ExecutorPlugin } from '@qlover/fe-corekit';
|
|
2
2
|
import type { LoggerInterface } from '@qlover/logger';
|
|
3
|
-
import { inject } from 'inversify';
|
|
3
|
+
import { inject, injectable } from 'inversify';
|
|
4
4
|
import { I18nService } from '../services/I18nService';
|
|
5
|
-
import { IOCIdentifier } from '
|
|
5
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* When throw error, it will be converted to i18n key
|
|
9
9
|
*
|
|
10
10
|
* If the error thrown is an i18n key, it will be converted to the corresponding text
|
|
11
11
|
*/
|
|
12
|
+
@injectable()
|
|
12
13
|
export class I18nKeyErrorPlugin implements ExecutorPlugin {
|
|
13
14
|
readonly pluginName = 'I18nKeyErrorPlugin';
|
|
14
15
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IOCIdentifierMap } from '@/core/IOC';
|
|
2
|
+
import {
|
|
3
|
+
IOCContainerInterface,
|
|
4
|
+
ServiceIdentifier
|
|
5
|
+
} from '@qlover/corekit-bridge';
|
|
6
|
+
import { Container } from 'inversify';
|
|
7
|
+
|
|
8
|
+
export class InversifyContainer implements IOCContainerInterface {
|
|
9
|
+
private container: Container;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.container = new Container({
|
|
13
|
+
// allow `@injectable` decorator, auto bind injectable classes
|
|
14
|
+
autobind: true,
|
|
15
|
+
// use singleton scope
|
|
16
|
+
defaultScope: 'Singleton'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
bind<T>(key: ServiceIdentifier<T>, value: T): void {
|
|
21
|
+
this.container.bind<T>(key).toConstantValue(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get<K extends keyof IOCIdentifierMap>(
|
|
25
|
+
serviceIdentifier: K
|
|
26
|
+
): IOCIdentifierMap[K];
|
|
27
|
+
get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
|
|
28
|
+
get<T, K extends keyof IOCIdentifierMap>(
|
|
29
|
+
serviceIdentifier: ServiceIdentifier<T> | K
|
|
30
|
+
): T | IOCIdentifierMap[K] {
|
|
31
|
+
return this.container.get<T>(serviceIdentifier);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
2
|
+
import type { RequestCatcherInterface } from '@qlover/corekit-bridge';
|
|
3
3
|
import type { RequestAdapterResponse } from '@qlover/fe-corekit';
|
|
4
4
|
import type { LoggerInterface } from '@qlover/logger';
|
|
5
5
|
import { inject, injectable } from 'inversify';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncExecutor, ExecutorPlugin } from '@qlover/fe-corekit';
|
|
2
|
-
import { IOCIdentifier } from '
|
|
2
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
3
3
|
import { injectable, inject } from 'inversify';
|
|
4
4
|
import type { LoggerInterface } from '@qlover/logger';
|
|
5
5
|
import { RouteService } from './RouteService';
|
|
@@ -3,7 +3,10 @@ import type { NavigateFunction, NavigateOptions } from 'react-router-dom';
|
|
|
3
3
|
import type { UIDependenciesInterface } from '@/base/port/UIDependenciesInterface';
|
|
4
4
|
import type { LoggerInterface } from '@qlover/logger';
|
|
5
5
|
import { I18nService } from '@/base/services/I18nService';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
StoreInterface,
|
|
8
|
+
type StoreStateInterface
|
|
9
|
+
} from '@qlover/corekit-bridge';
|
|
7
10
|
|
|
8
11
|
export type RouterServiceDependencies = {
|
|
9
12
|
navigate: NavigateFunction;
|
|
@@ -40,7 +43,7 @@ export class RouteService
|
|
|
40
43
|
|
|
41
44
|
if (!navigate) {
|
|
42
45
|
this.logger.debug(
|
|
43
|
-
'Please use `
|
|
46
|
+
'Please use `RouteService.setDependencies` to set dependencies'
|
|
44
47
|
);
|
|
45
48
|
}
|
|
46
49
|
|