@linyjs/plugin-docs 0.0.13 → 0.0.15
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 +19 -17
- package/docs/api-reference/index.md +91 -8
- package/docs/best-practices/error-handling.md +3 -3
- package/docs/best-practices/security.md +2 -2
- package/docs/faq.md +1 -1
- package/docs/getting-started/first-plugin.md +3 -3
- package/docs/guides/dependency-injection.md +574 -0
- package/docs/guides/permissions.md +1 -1
- package/docs/guides/server-module.md +92 -18
- package/docs/guides/shared-module-interface.md +349 -0
- package/examples/analytics-plugin/package.json +5 -5
- package/examples/analytics-plugin/src/server/index.ts +4 -4
- package/examples/blog-plugin/package.json +3 -3
- package/examples/blog-plugin/src/server/index.ts +4 -4
- package/examples/hello-world/package.json +2 -2
- package/examples/hello-world/src/server/index.ts +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
# 依赖注入机制
|
|
2
|
+
|
|
3
|
+
本指南详细说明 linyjs 框架的依赖注入(DI)机制,包括服务定义、依赖声明、构造函数约定以及跨模块引用规则。
|
|
4
|
+
|
|
5
|
+
> ⚠️ **最关键的规则**:服务类的构造函数**必须使用解构赋值**接收依赖。框架传入的是一个 `{ serviceTag: instance }` 对象,而非单个服务实例。详见 [构造函数约定](#构造函数约定)。
|
|
6
|
+
|
|
7
|
+
## 概述
|
|
8
|
+
|
|
9
|
+
linyjs 采用**构造函数注入**模式。框架在实例化服务时:
|
|
10
|
+
|
|
11
|
+
1. 读取 `IServiceDef.dependencies` 数组
|
|
12
|
+
2. 按依赖引用格式解析出对应的服务实例
|
|
13
|
+
3. 组装为一个 `{ [serviceTag]: instance }` 对象
|
|
14
|
+
4. 将该对象作为**唯一参数**传入服务类的构造函数
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────┐
|
|
18
|
+
│ IServiceDef │
|
|
19
|
+
│ ┌───────────────────────────────────────────┐ │
|
|
20
|
+
│ │ serviceTag: 'articleController' │ │
|
|
21
|
+
│ │ serviceImpl: ArticleController │ │
|
|
22
|
+
│ │ dependencies: ['mdb.mongoDBService'] │ │
|
|
23
|
+
│ │ meta: { usageType: 'controller' } │ │
|
|
24
|
+
│ └───────────────────────────────────────────┘ │
|
|
25
|
+
│ │ │
|
|
26
|
+
│ ┌───────────────┼───────────────┐ │
|
|
27
|
+
│ ▼ ▼ ▼ │
|
|
28
|
+
│ 框架解析依赖 查找服务实例 组装 depsRecord │
|
|
29
|
+
│ │
|
|
30
|
+
│ depsRecord = { │
|
|
31
|
+
│ mongoDBService: <MongoDBService 实例> │
|
|
32
|
+
│ } │
|
|
33
|
+
│ │ │
|
|
34
|
+
│ ▼ │
|
|
35
|
+
│ new ArticleController(depsRecord) │
|
|
36
|
+
│ // 构造函数内部解构取值 │
|
|
37
|
+
└─────────────────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 核心概念
|
|
41
|
+
|
|
42
|
+
### IServiceDef — 服务定义
|
|
43
|
+
|
|
44
|
+
每个服务通过 `IServiceDef` 对象声明:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import type { IServiceDef } from '@linyjs/server-module-interface';
|
|
48
|
+
|
|
49
|
+
const myServiceDef: IServiceDef = {
|
|
50
|
+
serviceTag: 'myService', // 服务唯一标识(模块内唯一)
|
|
51
|
+
serviceImpl: MyService, // 服务实现(class 或 factory function)
|
|
52
|
+
dependencies: ['otherService'], // 依赖的其他服务(可选)
|
|
53
|
+
meta: { usageType: 'normal_service' } // 元数据
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
58
|
+
|------|------|------|------|
|
|
59
|
+
| `serviceTag` | `string` | ✅ | 服务的唯一标识,模块内唯一 |
|
|
60
|
+
| `serviceImpl` | `class \| function` | ✅ | 服务实现,可以是类(constructor)或工厂函数 |
|
|
61
|
+
| `dependencies` | `string[]` | ❌ | 依赖的服务引用列表,支持模块内和跨模块格式 |
|
|
62
|
+
| `meta` | `Record<string, any>` | ❌ | 元数据,`usageType` 字段决定服务用途 |
|
|
63
|
+
|
|
64
|
+
### IModule — 模块定义
|
|
65
|
+
|
|
66
|
+
一个模块包含多个 `IServiceDef`:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import type { IModule } from '@linyjs/server-module-interface';
|
|
70
|
+
|
|
71
|
+
export const myServerModule: IModule = {
|
|
72
|
+
name: 'myServer', // 模块名称(全局唯一)
|
|
73
|
+
moduleDependencies: ['mdb'], // 依赖的其他模块(可选)
|
|
74
|
+
serviceDefs: [ // 服务定义列表
|
|
75
|
+
serviceDef,
|
|
76
|
+
controllerDef,
|
|
77
|
+
// ...
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
83
|
+
|------|------|------|------|
|
|
84
|
+
| `name` | `string` | ✅ | 模块名称,全局唯一,跨模块依赖引用时使用 |
|
|
85
|
+
| `moduleDependencies` | `string[]` | ❌ | 依赖的模块名称列表 |
|
|
86
|
+
| `serviceDefs` | `IServiceDef[]` | ✅ | 服务定义列表,顺序决定了实例化的先后 |
|
|
87
|
+
|
|
88
|
+
### meta.usageType — 服务用途
|
|
89
|
+
|
|
90
|
+
`meta.usageType` 决定了框架如何处理实例化后的服务:
|
|
91
|
+
|
|
92
|
+
| usageType | 说明 | 框架行为 |
|
|
93
|
+
|-----------|------|----------|
|
|
94
|
+
| `'controller'` | 控制器 | 自动调用 `getRoutes()` 注册路由 |
|
|
95
|
+
| `'middleware'` | 中间件 | 自动调用 `getMiddlewares()` 注册中间件 |
|
|
96
|
+
| `'cron'` | 定时任务 | 自动调用 `getCronJobs()` 注册定时任务 |
|
|
97
|
+
| `'normal_service'` | 普通服务 | 注册到服务池,供其他模块通过依赖注入使用 |
|
|
98
|
+
|
|
99
|
+
> 💡 `normal_service` 类型的服务不会自动注册路由等,但它会被注册到 `moduleName.serviceTag` 下,供其他模块依赖。
|
|
100
|
+
|
|
101
|
+
## 依赖声明
|
|
102
|
+
|
|
103
|
+
### 依赖引用格式
|
|
104
|
+
|
|
105
|
+
`dependencies` 数组中的每个字符串代表一个服务引用,支持两种格式:
|
|
106
|
+
|
|
107
|
+
| 格式 | 示例 | 说明 | 查找范围 |
|
|
108
|
+
|------|------|------|----------|
|
|
109
|
+
| 模块内引用 | `'myService'` | 不含 `.` | 当前模块的已实例化服务 |
|
|
110
|
+
| 跨模块引用 | `'mdb.mongoDBService'` | 含 `.`,格式为 `moduleName.serviceTag` | 指定模块的已实例化服务 |
|
|
111
|
+
|
|
112
|
+
### 模块内引用
|
|
113
|
+
|
|
114
|
+
在同一模块内,服务按 `serviceDefs` 数组顺序依次实例化。后声明的服务可以依赖先声明的服务:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// ✅ 正确:articleService 先声明,articleController 后声明
|
|
118
|
+
const myModule: IModule = {
|
|
119
|
+
name: 'myServer',
|
|
120
|
+
serviceDefs: [
|
|
121
|
+
{
|
|
122
|
+
serviceTag: 'articleService',
|
|
123
|
+
serviceImpl: ArticleService,
|
|
124
|
+
dependencies: ['mdb.mongoDBService'], // 跨模块引用
|
|
125
|
+
meta: { usageType: 'normal_service' }
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
serviceTag: 'articleController',
|
|
129
|
+
serviceImpl: ArticleController,
|
|
130
|
+
dependencies: ['articleService'], // 模块内引用
|
|
131
|
+
meta: { usageType: 'controller' }
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
> ⚠️ **顺序很重要**:被依赖的服务必须先在 `serviceDefs` 中声明,否则框架在实例化时找不到依赖会抛出错误。
|
|
138
|
+
|
|
139
|
+
### 跨模块引用
|
|
140
|
+
|
|
141
|
+
使用 `'moduleName.serviceTag'` 格式可以引用其他模块中的服务。要求:
|
|
142
|
+
|
|
143
|
+
1. 目标模块必须在当前模块之前加载(通过 `moduleDependencies` 声明)
|
|
144
|
+
2. 目标模块中的服务必须已经实例化
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// 引用 mdb 模块的 mongoDBService
|
|
148
|
+
const myServiceDef: IServiceDef = {
|
|
149
|
+
serviceTag: 'myService',
|
|
150
|
+
serviceImpl: MyService,
|
|
151
|
+
dependencies: ['mdb.mongoDBService'], // 跨模块引用
|
|
152
|
+
meta: { usageType: 'normal_service' }
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const myModule: IModule = {
|
|
156
|
+
name: 'myServer',
|
|
157
|
+
moduleDependencies: ['mdb'], // 声明模块级依赖
|
|
158
|
+
serviceDefs: [myServiceDef]
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 常见跨模块引用
|
|
163
|
+
|
|
164
|
+
linyjs 框架内置模块提供以下可注入服务:
|
|
165
|
+
|
|
166
|
+
| 依赖引用 | 接口类型 | 来源模块 | 说明 |
|
|
167
|
+
|----------|----------|----------|------|
|
|
168
|
+
| `'mdb.mongoDBService'` | `IMongoDBService` | mdb | MongoDB 数据库服务 |
|
|
169
|
+
| `'authServer.userService'` | `IUserService` | auth | 用户管理服务 |
|
|
170
|
+
| `'authServer.initService'` | - | auth | 系统初始化服务 |
|
|
171
|
+
| `'permissionServer.permissionService'` | `IPermissionService` | permission | 权限检查服务 |
|
|
172
|
+
|
|
173
|
+
> 💡 使用 `@linyjs/shared-module-interface` 包可以获取这些接口的 TypeScript 类型定义,详见 [shared-module-interface 指南](./shared-module-interface.md)。
|
|
174
|
+
|
|
175
|
+
## 构造函数约定
|
|
176
|
+
|
|
177
|
+
### ⚠️ 必须使用解构赋值
|
|
178
|
+
|
|
179
|
+
框架将所有依赖组装为一个对象传入构造函数。构造函数**必须解构**该对象才能获取各个服务实例。
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// ✅ 正确:解构赋值
|
|
183
|
+
class MyController {
|
|
184
|
+
private mongoDBService: IMongoDBService;
|
|
185
|
+
|
|
186
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
187
|
+
this.mongoDBService = mongoDBService; // mongoDBService 是服务实例
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// ❌ 错误:不解构,直接赋值
|
|
194
|
+
class MyController {
|
|
195
|
+
constructor(deps: any) {
|
|
196
|
+
this.mongoDBService = deps; // deps 是 { mongoDBService: <实例> },不是实例本身
|
|
197
|
+
// this.mongoDBService.getCollection → ❌ undefined is not a function
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 多个依赖的解构
|
|
203
|
+
|
|
204
|
+
当服务有多个依赖时,在解构中列出所有依赖:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
class MyController {
|
|
208
|
+
constructor(
|
|
209
|
+
private mongoDBService: IMongoDBService,
|
|
210
|
+
private userService: IUserService,
|
|
211
|
+
private permissionService: IPermissionService
|
|
212
|
+
) {}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 对应的 ServiceDef
|
|
216
|
+
const myControllerDef: IServiceDef = {
|
|
217
|
+
serviceTag: 'myController',
|
|
218
|
+
serviceImpl: MyController,
|
|
219
|
+
dependencies: [
|
|
220
|
+
'mdb.mongoDBService',
|
|
221
|
+
'authServer.userService',
|
|
222
|
+
'permissionServer.permissionService'
|
|
223
|
+
],
|
|
224
|
+
meta: { usageType: 'controller' }
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
> ⚠️ 上面的写法使用了 TypeScript 参数属性(`private mongoDBService`),但框架传入的是**一个对象**而非多个参数。正确的写法应该是:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// ✅ 正确:接收单个对象参数并解构
|
|
232
|
+
class MyController {
|
|
233
|
+
private mongoDBService: IMongoDBService;
|
|
234
|
+
private userService: IUserService;
|
|
235
|
+
private permissionService: IPermissionService;
|
|
236
|
+
|
|
237
|
+
constructor(deps: {
|
|
238
|
+
mongoDBService: IMongoDBService;
|
|
239
|
+
userService: IUserService;
|
|
240
|
+
permissionService: IPermissionService;
|
|
241
|
+
}) {
|
|
242
|
+
this.mongoDBService = deps.mongoDBService;
|
|
243
|
+
this.userService = deps.userService;
|
|
244
|
+
this.permissionService = deps.permissionService;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
或使用解构简写:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// ✅ 正确:解构简写
|
|
253
|
+
class MyController {
|
|
254
|
+
private mongoDBService: IMongoDBService;
|
|
255
|
+
private userService: IUserService;
|
|
256
|
+
private permissionService: IPermissionService;
|
|
257
|
+
|
|
258
|
+
constructor({ mongoDBService, userService, permissionService }: {
|
|
259
|
+
mongoDBService: IMongoDBService;
|
|
260
|
+
userService: IUserService;
|
|
261
|
+
permissionService: IPermissionService;
|
|
262
|
+
}) {
|
|
263
|
+
this.mongoDBService = mongoDBService;
|
|
264
|
+
this.userService = userService;
|
|
265
|
+
this.permissionService = permissionService;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 工厂函数
|
|
271
|
+
|
|
272
|
+
`serviceImpl` 也可以是工厂函数(非 class)。工厂函数同样接收 `depsRecord` 对象:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// 工厂函数方式
|
|
276
|
+
const myServiceDef: IServiceDef = {
|
|
277
|
+
serviceTag: 'myService',
|
|
278
|
+
serviceImpl: ({ mongoDBService }: { mongoDBService: IMongoDBService }) => {
|
|
279
|
+
// 返回服务对象
|
|
280
|
+
return {
|
|
281
|
+
async doSomething() {
|
|
282
|
+
const collection = mongoDBService.getCollection('items');
|
|
283
|
+
return await collection.find({}).toArray();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
},
|
|
287
|
+
dependencies: ['mdb.mongoDBService'],
|
|
288
|
+
meta: { usageType: 'normal_service' }
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
> 💡 框架通过检测 `prototype.constructor` 和 `class` 关键字来判断是 class 还是工厂函数。class 使用 `new` 调用,工厂函数直接调用。
|
|
293
|
+
|
|
294
|
+
## 实例化流程
|
|
295
|
+
|
|
296
|
+
框架加载模块时,按以下顺序处理:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
1. 遍历 serverModules
|
|
300
|
+
│
|
|
301
|
+
├── 2. 对每个模块,遍历 serviceDefs(按数组顺序)
|
|
302
|
+
│ │
|
|
303
|
+
│ ├── 3. 对每个 serviceDef,调用 instantiateService()
|
|
304
|
+
│ │ │
|
|
305
|
+
│ │ ├── 解析 dependencies 数组
|
|
306
|
+
│ │ │ ├── 模块内引用 → 从 loadedModule.services 查找
|
|
307
|
+
│ │ │ └── 跨模块引用 → 从 loadedModules 目标模块查找
|
|
308
|
+
│ │ │
|
|
309
|
+
│ │ ├── 组装 depsRecord = { serviceTag: instance, ... }
|
|
310
|
+
│ │ │
|
|
311
|
+
│ │ └── new serviceImpl(depsRecord) 或 serviceImpl(depsRecord)
|
|
312
|
+
│ │
|
|
313
|
+
│ ├── 4. 将实例存入 loadedModule.services.set(serviceTag, instance)
|
|
314
|
+
│ │
|
|
315
|
+
│ └── 5. 按 meta.usageType 分发
|
|
316
|
+
│ ├── controller → 注册路由
|
|
317
|
+
│ ├── middleware → 注册中间件
|
|
318
|
+
│ ├── cron → 注册定时任务
|
|
319
|
+
│ └── normal_service → 注册到服务池
|
|
320
|
+
│
|
|
321
|
+
└── 6. 所有 serviceDefs 处理完毕
|
|
322
|
+
│
|
|
323
|
+
└── 7. 遍历所有服务实例,调用 initialize()(如果存在)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 关键规则
|
|
327
|
+
|
|
328
|
+
1. **顺序依赖**:`serviceDefs` 数组中的顺序决定实例化顺序。被依赖的服务必须先声明。
|
|
329
|
+
2. **模块加载顺序**:跨模块依赖要求被依赖的模块先加载。通过 `moduleDependencies` 声明。
|
|
330
|
+
3. **单例模式**:每个 serviceDef 只实例化一次,在整个应用生命周期中是单例。
|
|
331
|
+
4. **初始化钩子**:如果服务实例有 `initialize()` 方法,框架会在所有服务实例化后自动调用。
|
|
332
|
+
|
|
333
|
+
## 完整示例
|
|
334
|
+
|
|
335
|
+
以下是一个完整的模块示例,展示模块内依赖和跨模块依赖的正确用法:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import type { IModule, IServiceDef, IControllerService } from '@linyjs/server-module-interface';
|
|
339
|
+
import type { IMongoDBService, IUserService } from '@linyjs/shared-module-interface';
|
|
340
|
+
|
|
341
|
+
// ==================== 服务层 ====================
|
|
342
|
+
|
|
343
|
+
class TodoService {
|
|
344
|
+
private mongoDBService: IMongoDBService;
|
|
345
|
+
|
|
346
|
+
// ✅ 解构赋值接收依赖
|
|
347
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
348
|
+
this.mongoDBService = mongoDBService;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async listTodos() {
|
|
352
|
+
const collection = this.mongoDBService.getCollection('todos');
|
|
353
|
+
return await collection.find({}).toArray();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async createTodo(data: { title: string; userId: string }) {
|
|
357
|
+
const collection = this.mongoDBService.getCollection('todos');
|
|
358
|
+
const now = new Date();
|
|
359
|
+
const result = await collection.insertOne({
|
|
360
|
+
...data,
|
|
361
|
+
completed: false,
|
|
362
|
+
createdAt: now,
|
|
363
|
+
updatedAt: now,
|
|
364
|
+
});
|
|
365
|
+
return { ...data, _id: result.insertedId, completed: false, createdAt: now, updatedAt: now };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ==================== 控制器层 ====================
|
|
370
|
+
|
|
371
|
+
class TodoController implements IControllerService {
|
|
372
|
+
private todoService: TodoService;
|
|
373
|
+
private userService: IUserService;
|
|
374
|
+
|
|
375
|
+
// ✅ 多依赖解构
|
|
376
|
+
constructor(deps: { todoService: TodoService; userService: IUserService }) {
|
|
377
|
+
this.todoService = deps.todoService;
|
|
378
|
+
this.userService = deps.userService;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
getRoutes() {
|
|
382
|
+
return [
|
|
383
|
+
{
|
|
384
|
+
method: 'GET' as const,
|
|
385
|
+
path: '/api/todos',
|
|
386
|
+
middlewares: ['auth'],
|
|
387
|
+
responseType: 'json' as const,
|
|
388
|
+
handler: async (params: Record<string, any>, ctx: any) => {
|
|
389
|
+
const todos = await this.todoService.listTodos();
|
|
390
|
+
return { success: true, data: todos };
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
method: 'POST' as const,
|
|
395
|
+
path: '/api/todos',
|
|
396
|
+
middlewares: ['auth'],
|
|
397
|
+
responseType: 'json' as const,
|
|
398
|
+
handler: async (params: Record<string, any>, ctx: any) => {
|
|
399
|
+
const user = ctx.user;
|
|
400
|
+
const todo = await this.todoService.createTodo({
|
|
401
|
+
title: params.title,
|
|
402
|
+
userId: user._id.toString(),
|
|
403
|
+
});
|
|
404
|
+
return { success: true, data: todo };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ==================== ServiceDef 声明 ====================
|
|
412
|
+
|
|
413
|
+
// 注意顺序:TodoService 先声明(被 TodoController 依赖)
|
|
414
|
+
const todoServiceDef: IServiceDef = {
|
|
415
|
+
serviceTag: 'todoService',
|
|
416
|
+
serviceImpl: TodoService,
|
|
417
|
+
dependencies: ['mdb.mongoDBService'], // 跨模块依赖
|
|
418
|
+
meta: { usageType: 'normal_service' }
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const todoControllerDef: IServiceDef = {
|
|
422
|
+
serviceTag: 'todoController',
|
|
423
|
+
serviceImpl: TodoController,
|
|
424
|
+
dependencies: [
|
|
425
|
+
'todoService', // 模块内依赖
|
|
426
|
+
'authServer.userService' // 跨模块依赖
|
|
427
|
+
],
|
|
428
|
+
meta: { usageType: 'controller' }
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// ==================== 模块导出 ====================
|
|
432
|
+
|
|
433
|
+
export const todoServerModule: IModule = {
|
|
434
|
+
name: 'todoServer',
|
|
435
|
+
moduleDependencies: ['mdb', 'authServer'], // 声明模块级依赖
|
|
436
|
+
serviceDefs: [todoServiceDef, todoControllerDef] // 顺序:先 service 后 controller
|
|
437
|
+
};
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## initialize 生命周期钩子
|
|
441
|
+
|
|
442
|
+
如果服务类实现了 `initialize()` 方法,框架会在模块所有服务实例化完毕后自动调用。适合做初始化逻辑:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
class InitService {
|
|
446
|
+
private mongoDBService: IMongoDBService;
|
|
447
|
+
|
|
448
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
449
|
+
this.mongoDBService = mongoDBService;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// 框架会自动调用此方法
|
|
453
|
+
async initialize() {
|
|
454
|
+
console.log('[MyModule] Initializing...');
|
|
455
|
+
|
|
456
|
+
// 创建索引
|
|
457
|
+
const collection = this.mongoDBService.getCollection('items');
|
|
458
|
+
await collection.createIndex({ name: 1 }, { unique: true });
|
|
459
|
+
|
|
460
|
+
// 初始化默认数据
|
|
461
|
+
const count = await collection.countDocuments();
|
|
462
|
+
if (count === 0) {
|
|
463
|
+
await collection.insertOne({ name: 'default', createdAt: new Date() });
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
console.log('[MyModule] Initialization complete');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const initServiceDef: IServiceDef = {
|
|
471
|
+
serviceTag: 'initService',
|
|
472
|
+
serviceImpl: InitService,
|
|
473
|
+
dependencies: ['mdb.mongoDBService'],
|
|
474
|
+
meta: { usageType: 'normal_service' }
|
|
475
|
+
};
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## 常见错误
|
|
479
|
+
|
|
480
|
+
### 1. 构造函数未解构依赖
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
// ❌ 错误
|
|
484
|
+
class MyController {
|
|
485
|
+
constructor(deps: any) {
|
|
486
|
+
this.service = deps; // deps 是 { service: <实例> }
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// 运行时报错:this.service.someMethod is not a function
|
|
490
|
+
|
|
491
|
+
// ✅ 正确
|
|
492
|
+
class MyController {
|
|
493
|
+
constructor({ service }: { service: SomeService }) {
|
|
494
|
+
this.service = service; // service 是实例本身
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### 2. 依赖顺序错误
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// ❌ 错误:controller 依赖 service,但 controller 先声明
|
|
503
|
+
const myModule: IModule = {
|
|
504
|
+
name: 'myServer',
|
|
505
|
+
serviceDefs: [
|
|
506
|
+
{ serviceTag: 'myController', serviceImpl: MyController, dependencies: ['myService'], meta: { usageType: 'controller' } },
|
|
507
|
+
{ serviceTag: 'myService', serviceImpl: MyService, meta: { usageType: 'normal_service' } }
|
|
508
|
+
// ↑ 报错:Service not found in current module: myService
|
|
509
|
+
]
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
// ✅ 正确:service 先声明
|
|
513
|
+
const myModule: IModule = {
|
|
514
|
+
name: 'myServer',
|
|
515
|
+
serviceDefs: [
|
|
516
|
+
{ serviceTag: 'myService', serviceImpl: MyService, meta: { usageType: 'normal_service' } },
|
|
517
|
+
{ serviceTag: 'myController', serviceImpl: MyController, dependencies: ['myService'], meta: { usageType: 'controller' } }
|
|
518
|
+
]
|
|
519
|
+
};
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### 3. 跨模块依赖未声明 moduleDependencies
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// ❌ 错误:依赖了 mdb 模块但未声明
|
|
526
|
+
const myModule: IModule = {
|
|
527
|
+
name: 'myServer',
|
|
528
|
+
// 缺少 moduleDependencies: ['mdb']
|
|
529
|
+
serviceDefs: [
|
|
530
|
+
{ serviceTag: 'myService', serviceImpl: MyService, dependencies: ['mdb.mongoDBService'], meta: { usageType: 'normal_service' } }
|
|
531
|
+
// ↑ 可能报错:Module not found: mdb
|
|
532
|
+
]
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// ✅ 正确
|
|
536
|
+
const myModule: IModule = {
|
|
537
|
+
name: 'myServer',
|
|
538
|
+
moduleDependencies: ['mdb'], // 显式声明模块依赖
|
|
539
|
+
serviceDefs: [/* ... */]
|
|
540
|
+
};
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 4. 使用 TypeScript 参数属性(constructor(private x))
|
|
544
|
+
|
|
545
|
+
TypeScript 的参数属性语法 `constructor(private x: Type)` 会被编译为多个构造函数参数,与框架的单对象参数约定不兼容:
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
// ❌ 错误:TypeScript 参数属性在编译后会生成 this.x = arguments[0]
|
|
549
|
+
// 框架传入的是一个对象 { x: instance },会被整体赋给 this.x
|
|
550
|
+
class MyController {
|
|
551
|
+
constructor(private mongoDBService: IMongoDBService) {}
|
|
552
|
+
// 编译后:this.mongoDBService = { mongoDBService: <实例> }(整个对象)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// ✅ 正确:显式声明属性并解构
|
|
556
|
+
class MyController {
|
|
557
|
+
private mongoDBService: IMongoDBService;
|
|
558
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
559
|
+
this.mongoDBService = mongoDBService;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## 相关文档
|
|
565
|
+
|
|
566
|
+
- 📖 [服务端模块开发](./server-module.md) — 控制器、中间件、定时任务
|
|
567
|
+
- 📦 [shared-module-interface 指南](./shared-module-interface.md) — 类型定义包使用说明
|
|
568
|
+
- 💾 [数据持久化](./data-persistence.md) — MongoDB 服务使用
|
|
569
|
+
- 🔒 [权限管理](./permissions.md) — 权限服务使用
|
|
570
|
+
- 🔧 [API 参考](../api-reference/index.md) — 接口类型定义
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
**上一篇**: [服务端模块开发](./server-module.md) | **下一篇**: [shared-module-interface 指南](./shared-module-interface.md)
|