@linyjs/plugin-docs 0.0.13 → 0.0.14
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
|
@@ -73,34 +73,46 @@ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
|
|
73
73
|
|
|
74
74
|
### 路由参数
|
|
75
75
|
|
|
76
|
+
框架在调用 `handler` 时,会将 **URL 路径参数**、**查询字符串** 和 **请求体** 合并为一个 `params` 对象传入:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// 框架内部实现(简化):
|
|
80
|
+
const params = { ...req.params, ...req.query, ...req.body }
|
|
81
|
+
await route.handler(params, ctx)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
因此在 handler 中可以直接从 `params` 上获取这三类数据,无需区分来源:
|
|
85
|
+
|
|
76
86
|
```typescript
|
|
77
|
-
// 路径参数: /api/items/:id
|
|
87
|
+
// 路径参数: GET /api/items/:id
|
|
78
88
|
{
|
|
79
89
|
path: '/api/items/:id',
|
|
80
90
|
handler: async (params) => {
|
|
81
|
-
const id = params.id; // 从 URL
|
|
91
|
+
const id = params.id; // ✅ 从 URL 路径获取
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
|
|
85
|
-
// 查询参数: /api/items?page=1&limit=10
|
|
95
|
+
// 查询参数: GET /api/items?page=1&limit=10
|
|
86
96
|
{
|
|
87
97
|
path: '/api/items',
|
|
88
98
|
handler: async (params) => {
|
|
89
|
-
const page = params.
|
|
90
|
-
const limit = params.
|
|
99
|
+
const page = params.page; // ✅ 从查询字符串获取
|
|
100
|
+
const limit = params.limit; // ✅ 从查询字符串获取
|
|
91
101
|
}
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
// 请求体: POST /api/items with JSON body
|
|
104
|
+
// 请求体: POST /api/items with JSON body { "name": "..." }
|
|
95
105
|
{
|
|
96
106
|
method: 'POST',
|
|
97
107
|
path: '/api/items',
|
|
98
108
|
handler: async (params) => {
|
|
99
|
-
const { name, description } = params
|
|
109
|
+
const { name, description } = params; // ✅ 从请求体获取
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
```
|
|
103
113
|
|
|
114
|
+
> 💡 **合并顺序**:`req.params` → `req.query` → `req.body`。如果三者存在同名字段,后者会覆盖前者。通常建议避免命名冲突。
|
|
115
|
+
|
|
104
116
|
### 响应类型
|
|
105
117
|
|
|
106
118
|
框架支持三种响应类型:
|
|
@@ -244,7 +256,9 @@ export const cronDef = {
|
|
|
244
256
|
|
|
245
257
|
## 依赖注入
|
|
246
258
|
|
|
247
|
-
|
|
259
|
+
服务之间可以通过依赖注入互相引用。框架采用**构造函数注入**模式,将依赖组装为一个对象传入构造函数。
|
|
260
|
+
|
|
261
|
+
> 📖 **完整说明**:依赖注入的详细机制(包括跨模块引用、实例化流程、常见错误等)请阅读 [依赖注入机制](./dependency-injection.md)。
|
|
248
262
|
|
|
249
263
|
### 声明依赖
|
|
250
264
|
|
|
@@ -252,16 +266,27 @@ export const cronDef = {
|
|
|
252
266
|
export const myServiceDef = {
|
|
253
267
|
serviceTag: 'myService',
|
|
254
268
|
serviceImpl: MyService,
|
|
255
|
-
dependencies: ['otherService'], //
|
|
269
|
+
dependencies: ['otherService'], // 依赖其他服务(模块内引用)
|
|
256
270
|
meta: { usageType: 'normal_service' }
|
|
257
271
|
};
|
|
258
272
|
```
|
|
259
273
|
|
|
274
|
+
依赖引用支持两种格式:
|
|
275
|
+
- **模块内引用**:`'serviceTag'`(不含点号)
|
|
276
|
+
- **跨模块引用**:`'moduleName.serviceTag'`(如 `'mdb.mongoDBService'`)
|
|
277
|
+
|
|
260
278
|
### 使用依赖
|
|
261
279
|
|
|
280
|
+
> ⚠️ **关键**:构造函数**必须使用解构赋值**接收依赖。框架传入的是 `{ serviceTag: instance }` 对象,而非单个服务实例。
|
|
281
|
+
|
|
262
282
|
```typescript
|
|
283
|
+
// ✅ 正确:解构赋值
|
|
263
284
|
class MyService {
|
|
264
|
-
|
|
285
|
+
private otherService: SomeService;
|
|
286
|
+
|
|
287
|
+
constructor({ otherService }: { otherService: SomeService }) {
|
|
288
|
+
this.otherService = otherService;
|
|
289
|
+
}
|
|
265
290
|
|
|
266
291
|
async doSomething() {
|
|
267
292
|
// 使用依赖的服务
|
|
@@ -269,6 +294,42 @@ class MyService {
|
|
|
269
294
|
return result;
|
|
270
295
|
}
|
|
271
296
|
}
|
|
297
|
+
|
|
298
|
+
// ❌ 错误:不解构,直接赋值
|
|
299
|
+
class MyService {
|
|
300
|
+
constructor(deps: any) {
|
|
301
|
+
this.otherService = deps; // deps 是 { otherService: <实例> },不是实例本身
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 跨模块依赖
|
|
307
|
+
|
|
308
|
+
引用其他模块(如 mdb、auth)的服务时,需要:
|
|
309
|
+
1. 在 `moduleDependencies` 中声明模块依赖
|
|
310
|
+
2. 在 `dependencies` 中使用 `'moduleName.serviceTag'` 格式
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
314
|
+
|
|
315
|
+
class MyService {
|
|
316
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
317
|
+
this.mongoDBService = mongoDBService;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const myServiceDef = {
|
|
322
|
+
serviceTag: 'myService',
|
|
323
|
+
serviceImpl: MyService,
|
|
324
|
+
dependencies: ['mdb.mongoDBService'], // 跨模块引用
|
|
325
|
+
meta: { usageType: 'normal_service' }
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
export const myServerModule: IModule = {
|
|
329
|
+
name: 'myServer',
|
|
330
|
+
moduleDependencies: ['mdb'], // 声明模块级依赖
|
|
331
|
+
serviceDefs: [myServiceDef]
|
|
332
|
+
};
|
|
272
333
|
```
|
|
273
334
|
|
|
274
335
|
## 错误处理
|
|
@@ -294,7 +355,7 @@ handler: async (params, ctx) => {
|
|
|
294
355
|
|
|
295
356
|
```typescript
|
|
296
357
|
handler: async (params, ctx) => {
|
|
297
|
-
if (!params.
|
|
358
|
+
if (!params.name) {
|
|
298
359
|
throw new Error('Name is required'); // 自动返回 400
|
|
299
360
|
}
|
|
300
361
|
}
|
|
@@ -326,8 +387,8 @@ interface CreateItemRequest {
|
|
|
326
387
|
description?: string;
|
|
327
388
|
}
|
|
328
389
|
|
|
329
|
-
handler: async (params:
|
|
330
|
-
const { name, description } = params
|
|
390
|
+
handler: async (params: CreateItemRequest) => {
|
|
391
|
+
const { name, description } = params;
|
|
331
392
|
}
|
|
332
393
|
```
|
|
333
394
|
|
|
@@ -335,7 +396,7 @@ handler: async (params: { body: CreateItemRequest }) => {
|
|
|
335
396
|
|
|
336
397
|
```typescript
|
|
337
398
|
handler: async (params) => {
|
|
338
|
-
const { email } = params
|
|
399
|
+
const { email } = params;
|
|
339
400
|
|
|
340
401
|
if (!isValidEmail(email)) {
|
|
341
402
|
return {
|
|
@@ -350,11 +411,23 @@ handler: async (params) => {
|
|
|
350
411
|
|
|
351
412
|
```typescript
|
|
352
413
|
class MyController {
|
|
353
|
-
|
|
414
|
+
private logger: any;
|
|
415
|
+
|
|
416
|
+
constructor({ logger }: { logger: any }) {
|
|
417
|
+
this.logger = logger;
|
|
418
|
+
}
|
|
354
419
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
420
|
+
getRoutes() {
|
|
421
|
+
return [{
|
|
422
|
+
method: 'GET' as const,
|
|
423
|
+
path: '/api/items',
|
|
424
|
+
middlewares: [],
|
|
425
|
+
responseType: 'json' as const,
|
|
426
|
+
handler: async (params) => {
|
|
427
|
+
this.logger.info('Processing request...');
|
|
428
|
+
// ...
|
|
429
|
+
}
|
|
430
|
+
}];
|
|
358
431
|
}
|
|
359
432
|
}
|
|
360
433
|
```
|
|
@@ -368,6 +441,7 @@ class MyController {
|
|
|
368
441
|
|
|
369
442
|
## 下一步
|
|
370
443
|
|
|
444
|
+
- 📖 阅读 [依赖注入机制](./dependency-injection.md) — 完整的 DI 机制说明
|
|
371
445
|
- 💻 阅读 [客户端模块开发](./client-module.md)
|
|
372
446
|
- 💾 阅读 [数据持久化](./data-persistence.md)
|
|
373
447
|
- 🔒 阅读 [权限管理](./permissions.md)
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# @linyjs/shared-module-interface
|
|
2
|
+
|
|
3
|
+
本指南介绍 `@linyjs/shared-module-interface` 包的用途和使用方法。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
`@linyjs/shared-module-interface` 是一个**纯类型包**,汇集了 linyjs 框架所有公共模块(mdb、auth、permission)的 TypeScript 接口和类型定义。
|
|
8
|
+
|
|
9
|
+
### 为什么需要这个包?
|
|
10
|
+
|
|
11
|
+
linyjs 插件作为独立的 npm 包或 git 仓库开发,通过依赖注入机制获取 `IMongoDBService` 等服务实例。但 TypeScript 编译器需要类型声明才能提供:
|
|
12
|
+
|
|
13
|
+
- ✅ 构造函数参数的类型提示
|
|
14
|
+
- ✅ 服务方法的自动补全
|
|
15
|
+
- ✅ 编译时类型检查
|
|
16
|
+
|
|
17
|
+
如果每个插件都单独安装 `@linyjs/mdb`、`@linyjs/auth` 等包来获取类型,会带来:
|
|
18
|
+
|
|
19
|
+
- 📦 **包体积增大** — 插件打包了不需要的运行时代码
|
|
20
|
+
- ⚠️ **版本冲突** — 插件与框架使用的模块版本不一致
|
|
21
|
+
- 🔧 **配置冗余** — 每个插件都要在 `package.json` 中声明多个依赖
|
|
22
|
+
|
|
23
|
+
`@linyjs/shared-module-interface` 通过**纯类型导出**解决这些问题——只提供类型声明,不包含任何运行时代码。
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
在插件的 `package.json` 中添加为**开发依赖**:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@linyjs/shared-module-interface": "^0.0.1"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> ⚠️ 必须是 `devDependencies`,不是 `dependencies`。此包只包含 TypeScript 类型,编译后会被完全擦除,不应出现在运行时依赖中。
|
|
38
|
+
|
|
39
|
+
## 使用方式
|
|
40
|
+
|
|
41
|
+
### 导入类型
|
|
42
|
+
|
|
43
|
+
所有导出都使用 `import type` 语法,确保编译后类型被完全擦除:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import type { IMongoDBService, IUserService, IPermissionService } from '@linyjs/shared-module-interface';
|
|
47
|
+
import type { ObjectId, Db, Collection, Document, Filter } from '@linyjs/shared-module-interface';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 在依赖注入中使用
|
|
51
|
+
|
|
52
|
+
结合 linyjs 的依赖注入机制,在构造函数中使用类型:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
56
|
+
|
|
57
|
+
class ArticleService {
|
|
58
|
+
private mongoDBService: IMongoDBService;
|
|
59
|
+
|
|
60
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
61
|
+
this.mongoDBService = mongoDBService;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getArticleById(id: string) {
|
|
65
|
+
const collection = this.mongoDBService.getCollection('articles');
|
|
66
|
+
// ✅ IMongoDBService 提供完整的类型提示
|
|
67
|
+
return await collection.findOne({
|
|
68
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> 📖 关于依赖注入机制的完整说明,请阅读 [依赖注入机制](./dependency-injection.md)。
|
|
75
|
+
|
|
76
|
+
## 可用类型一览
|
|
77
|
+
|
|
78
|
+
### mdb 模块类型
|
|
79
|
+
|
|
80
|
+
| 类型 | 说明 | 运行时来源 |
|
|
81
|
+
|------|------|------------|
|
|
82
|
+
| `IMongoDBService` | MongoDB 服务接口 | `@linyjs/mdb` 模块通过 `mdb.mongoDBService` 注入 |
|
|
83
|
+
| `ObjectId` | MongoDB ObjectId 类型 | mongodb 包(运行时通过 `service.ObjectId` 获取) |
|
|
84
|
+
| `Db` | MongoDB 数据库实例类型 | mongodb 包 |
|
|
85
|
+
| `Collection` | MongoDB 集合类型 | mongodb 包 |
|
|
86
|
+
| `Document` | MongoDB 文档类型 | mongodb 包 |
|
|
87
|
+
| `Filter` | 查询过滤器类型 | mongodb 包 |
|
|
88
|
+
| `Sort` | 排序类型 | mongodb 包 |
|
|
89
|
+
| `UpdateFilter` | 更新操作类型 | mongodb 包 |
|
|
90
|
+
| `InsertOneResult` | 插入结果类型 | mongodb 包 |
|
|
91
|
+
| `UpdateResult` | 更新结果类型 | mongodb 包 |
|
|
92
|
+
| `DeleteResult` | 删除结果类型 | mongodb 包 |
|
|
93
|
+
| ... | mongodb 包的全部类型 | mongodb 包 |
|
|
94
|
+
|
|
95
|
+
#### IMongoDBService 接口
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface IMongoDBService {
|
|
99
|
+
/** MongoDB ObjectId 构造器(运行时通过此属性获取,无需单独安装 mongodb) */
|
|
100
|
+
readonly ObjectId: typeof ObjectId;
|
|
101
|
+
|
|
102
|
+
/** 获取数据库实例 */
|
|
103
|
+
getDb(): Db;
|
|
104
|
+
|
|
105
|
+
/** 获取指定集合 */
|
|
106
|
+
getCollection<T extends Document = Document>(collectionName: string): Collection<T>;
|
|
107
|
+
|
|
108
|
+
/** 检查数据库连接状态 */
|
|
109
|
+
isConnected(): boolean;
|
|
110
|
+
|
|
111
|
+
/** 关闭数据库连接 */
|
|
112
|
+
close(): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### auth 模块类型
|
|
117
|
+
|
|
118
|
+
| 类型 | 说明 |
|
|
119
|
+
|------|------|
|
|
120
|
+
| `IUserService` | 用户管理服务接口 |
|
|
121
|
+
| `IAuthUser` | 完整用户信息(含密码哈希,仅内部使用) |
|
|
122
|
+
| `ISafeUser` | 安全用户信息(不含密码哈希) |
|
|
123
|
+
| `IJwtPayload` | JWT Payload 结构 |
|
|
124
|
+
| `IAuthResult` | 认证结果(token + user) |
|
|
125
|
+
| `IsRootUser` | 判断用户是否为 Root 角色的函数类型 |
|
|
126
|
+
| `IsAdminUser` | 判断用户是否为 Admin 角色的函数类型 |
|
|
127
|
+
|
|
128
|
+
#### IUserService 接口
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
interface IUserService {
|
|
132
|
+
/** 获取所有用户列表(不含密码哈希) */
|
|
133
|
+
getAllUsers(): Promise<ISafeUser[]>;
|
|
134
|
+
|
|
135
|
+
/** 更新用户角色 */
|
|
136
|
+
updateUserRoles(userId: string, roles: string[]): Promise<ISafeUser | null>;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> 依赖声明:`dependencies: ['authServer.userService']`
|
|
141
|
+
|
|
142
|
+
### permission 模块类型
|
|
143
|
+
|
|
144
|
+
| 类型 | 说明 |
|
|
145
|
+
|------|------|
|
|
146
|
+
| `IPermissionService` | 权限服务接口 |
|
|
147
|
+
| `PermissionString` | 权限字符串格式(`resource:action`) |
|
|
148
|
+
| `Role` | 用户角色类型 |
|
|
149
|
+
| `IPermissionUser` | 权限模块使用的用户接口 |
|
|
150
|
+
| `IPermissionPolicy` | 权限策略定义 |
|
|
151
|
+
| `IPermissionCheckResult` | 权限检查结果 |
|
|
152
|
+
|
|
153
|
+
#### IPermissionService 接口
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface IPermissionService {
|
|
157
|
+
/** 检查用户是否有权限执行某个操作 */
|
|
158
|
+
check(
|
|
159
|
+
user: IPermissionUser | null,
|
|
160
|
+
permission: PermissionString,
|
|
161
|
+
context?: Record<string, any>
|
|
162
|
+
): Promise<IPermissionCheckResult>;
|
|
163
|
+
|
|
164
|
+
/** 注册权限策略 */
|
|
165
|
+
registerPolicy(policy: IPermissionPolicy): void;
|
|
166
|
+
|
|
167
|
+
/** 批量检查权限 */
|
|
168
|
+
checkMany(
|
|
169
|
+
user: IPermissionUser | null,
|
|
170
|
+
permissions: PermissionString[],
|
|
171
|
+
context?: Record<string, any>
|
|
172
|
+
): Promise<Record<PermissionString, IPermissionCheckResult>>;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> 依赖声明:`dependencies: ['permissionServer.permissionService']`
|
|
177
|
+
|
|
178
|
+
## ObjectId 的运行时使用
|
|
179
|
+
|
|
180
|
+
`@linyjs/shared-module-interface` 是纯类型包,`ObjectId` 等类型导入在编译后会被擦除。运行时需要通过注入的 `IMongoDBService` 实例获取 `ObjectId` 构造器:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import type { IMongoDBService, ObjectId } from '@linyjs/shared-module-interface';
|
|
184
|
+
|
|
185
|
+
class ArticleService {
|
|
186
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
187
|
+
this.mongoDBService = mongoDBService;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getArticleById(id: string) {
|
|
191
|
+
const collection = this.mongoDBService.getCollection('articles');
|
|
192
|
+
// ✅ 运行时通过 service.ObjectId 获取构造器
|
|
193
|
+
const article = await collection.findOne({
|
|
194
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
195
|
+
});
|
|
196
|
+
return article;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async getArticleWithType(id: string): Promise<{ _id: ObjectId; title: string } | null> {
|
|
200
|
+
const collection = this.mongoDBService.getCollection('articles');
|
|
201
|
+
// ✅ ObjectId 作为类型注解使用(编译后擦除)
|
|
202
|
+
const oid: ObjectId = new this.mongoDBService.ObjectId(id);
|
|
203
|
+
return await collection.findOne({ _id: oid });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 在服务类外部使用 ObjectId
|
|
209
|
+
|
|
210
|
+
如果需要在服务类外部(如工具函数、模型层)创建 `ObjectId`,可以将 `mongoDBService` 引用保存到模块级变量:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// db.ts
|
|
214
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
215
|
+
|
|
216
|
+
let mongoDBService: IMongoDBService | null = null;
|
|
217
|
+
|
|
218
|
+
export function setMongoDBService(service: IMongoDBService) {
|
|
219
|
+
mongoDBService = service;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function createObjectId(id: string) {
|
|
223
|
+
if (!mongoDBService) throw new Error('MongoDB service not initialized');
|
|
224
|
+
return new mongoDBService.ObjectId(id);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function getCollection(name: string) {
|
|
228
|
+
if (!mongoDBService) throw new Error('MongoDB service not initialized');
|
|
229
|
+
return mongoDBService.getCollection(name);
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// service.ts
|
|
235
|
+
import { setMongoDBService, createObjectId, getCollection } from './db.js';
|
|
236
|
+
|
|
237
|
+
class ArticleService {
|
|
238
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
239
|
+
this.mongoDBService = mongoDBService;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async initialize() {
|
|
243
|
+
// 将 mongoDBService 保存到模块级变量,供工具函数使用
|
|
244
|
+
setMongoDBService(this.mongoDBService);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async getArticleById(id: string) {
|
|
248
|
+
const collection = getCollection('articles');
|
|
249
|
+
return await collection.findOne({ _id: createObjectId(id) });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## 可注入服务汇总
|
|
255
|
+
|
|
256
|
+
| 依赖引用 | 接口类型 | 来源模块 | 说明 |
|
|
257
|
+
|----------|----------|----------|------|
|
|
258
|
+
| `'mdb.mongoDBService'` | `IMongoDBService` | mdb | MongoDB 数据库服务 |
|
|
259
|
+
| `'authServer.userService'` | `IUserService` | auth | 用户管理服务 |
|
|
260
|
+
| `'authServer.initService'` | - | auth | 系统初始化服务 |
|
|
261
|
+
| `'permissionServer.permissionService'` | `IPermissionService` | permission | 权限检查服务 |
|
|
262
|
+
|
|
263
|
+
> 💡 使用这些服务时,需要在模块定义中声明 `moduleDependencies`。
|
|
264
|
+
|
|
265
|
+
## 完整示例
|
|
266
|
+
|
|
267
|
+
以下示例展示如何同时注入多个基础模块服务:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import type { IModule, IServiceDef, IControllerService } from '@linyjs/server-module-interface';
|
|
271
|
+
import type {
|
|
272
|
+
IMongoDBService,
|
|
273
|
+
IUserService,
|
|
274
|
+
IPermissionService,
|
|
275
|
+
ISafeUser
|
|
276
|
+
} from '@linyjs/shared-module-interface';
|
|
277
|
+
|
|
278
|
+
class AdminController implements IControllerService {
|
|
279
|
+
private mongoDBService: IMongoDBService;
|
|
280
|
+
private userService: IUserService;
|
|
281
|
+
private permissionService: IPermissionService;
|
|
282
|
+
|
|
283
|
+
constructor(deps: {
|
|
284
|
+
mongoDBService: IMongoDBService;
|
|
285
|
+
userService: IUserService;
|
|
286
|
+
permissionService: IPermissionService;
|
|
287
|
+
}) {
|
|
288
|
+
this.mongoDBService = deps.mongoDBService;
|
|
289
|
+
this.userService = deps.userService;
|
|
290
|
+
this.permissionService = deps.permissionService;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
getRoutes() {
|
|
294
|
+
return [
|
|
295
|
+
{
|
|
296
|
+
method: 'GET' as const,
|
|
297
|
+
path: '/api/admin/users',
|
|
298
|
+
middlewares: ['auth'],
|
|
299
|
+
responseType: 'json' as const,
|
|
300
|
+
handler: async (params: Record<string, any>, ctx: any) => {
|
|
301
|
+
const user = ctx.user as ISafeUser;
|
|
302
|
+
|
|
303
|
+
// 使用权限服务检查权限
|
|
304
|
+
const result = await this.permissionService.check(
|
|
305
|
+
user,
|
|
306
|
+
'admin:read'
|
|
307
|
+
);
|
|
308
|
+
if (!result.allowed) {
|
|
309
|
+
return { status: 403, data: { error: result.reason } };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 使用用户服务获取用户列表
|
|
313
|
+
const users = await this.userService.getAllUsers();
|
|
314
|
+
return { success: true, data: users };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const adminControllerDef: IServiceDef = {
|
|
322
|
+
serviceTag: 'adminController',
|
|
323
|
+
serviceImpl: AdminController,
|
|
324
|
+
dependencies: [
|
|
325
|
+
'mdb.mongoDBService',
|
|
326
|
+
'authServer.userService',
|
|
327
|
+
'permissionServer.permissionService'
|
|
328
|
+
],
|
|
329
|
+
meta: { usageType: 'controller' }
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export const adminServerModule: IModule = {
|
|
333
|
+
name: 'adminServer',
|
|
334
|
+
moduleDependencies: ['mdb', 'authServer', 'permissionServer'],
|
|
335
|
+
serviceDefs: [adminControllerDef]
|
|
336
|
+
};
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## 相关文档
|
|
340
|
+
|
|
341
|
+
- 📖 [依赖注入机制](./dependency-injection.md) — DI 的完整说明
|
|
342
|
+
- 💾 [数据持久化](./data-persistence.md) — MongoDB 服务详细使用
|
|
343
|
+
- 🔐 [用户认证](./authentication.md) — 认证模块使用
|
|
344
|
+
- 🛡️ [权限管理](./permissions.md) — 权限模块使用
|
|
345
|
+
- 🔧 [API 参考](../api-reference/index.md) — 接口类型定义
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
**上一篇**: [依赖注入机制](./dependency-injection.md) | **下一篇**: [客户端模块开发](./client-module.md)
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"author": "Linyjs Team",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@linyjs/server-module-interface": "
|
|
17
|
-
"@linyjs/client-module-interface": "
|
|
18
|
-
"@linyjs/ui-slots-interface": "
|
|
16
|
+
"@linyjs/server-module-interface": "latest",
|
|
17
|
+
"@linyjs/client-module-interface": "latest",
|
|
18
|
+
"@linyjs/ui-slots-interface": "latest"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@linyjs/server-module-interface": "
|
|
22
|
-
"@linyjs/client-module-interface": "
|
|
21
|
+
"@linyjs/server-module-interface": "latest",
|
|
22
|
+
"@linyjs/client-module-interface": "latest"
|
|
23
23
|
},
|
|
24
24
|
"linyjs": {
|
|
25
25
|
"compatibleVersions": ["0.8.x"],
|
|
@@ -158,7 +158,7 @@ class AnalyticsController {
|
|
|
158
158
|
responseType: 'json' as const,
|
|
159
159
|
handler: async (params: any, ctx: any) => {
|
|
160
160
|
try {
|
|
161
|
-
const { eventName, userId, data } = params
|
|
161
|
+
const { eventName, userId, data } = params;
|
|
162
162
|
|
|
163
163
|
if (!eventName || !userId) {
|
|
164
164
|
return {
|
|
@@ -186,8 +186,8 @@ class AnalyticsController {
|
|
|
186
186
|
responseType: 'json' as const,
|
|
187
187
|
handler: async (params: any, ctx: any) => {
|
|
188
188
|
try {
|
|
189
|
-
const startDate = params.
|
|
190
|
-
const endDate = params.
|
|
189
|
+
const startDate = params.start || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
190
|
+
const endDate = params.end || new Date().toISOString();
|
|
191
191
|
|
|
192
192
|
const result = await this.analyticsService.getAnalytics(startDate, endDate);
|
|
193
193
|
return result;
|
|
@@ -227,7 +227,7 @@ class AnalyticsController {
|
|
|
227
227
|
responseType: 'json' as const,
|
|
228
228
|
handler: async (params: any, ctx: any) => {
|
|
229
229
|
try {
|
|
230
|
-
const days = parseInt(params.
|
|
230
|
+
const days = parseInt(params.days || '7');
|
|
231
231
|
const result = await this.analyticsService.getUserRetention(days);
|
|
232
232
|
return result;
|
|
233
233
|
} catch (error: any) {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"pack": "npx @linyjs/cli pack"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@linyjs/server-module-interface": "
|
|
13
|
-
"@linyjs/client-module-interface": "
|
|
14
|
-
"@linyjs/mdb": "
|
|
12
|
+
"@linyjs/server-module-interface": "latest",
|
|
13
|
+
"@linyjs/client-module-interface": "latest",
|
|
14
|
+
"@linyjs/mdb": "latest",
|
|
15
15
|
"react": "^19.0.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
@@ -103,8 +103,8 @@ class ArticleController {
|
|
|
103
103
|
middlewares: [],
|
|
104
104
|
responseType: 'json' as const,
|
|
105
105
|
handler: async (params: any, ctx: any) => {
|
|
106
|
-
const page = parseInt(params.
|
|
107
|
-
const limit = parseInt(params.
|
|
106
|
+
const page = parseInt(params.page || '1');
|
|
107
|
+
const limit = parseInt(params.limit || '10');
|
|
108
108
|
|
|
109
109
|
try {
|
|
110
110
|
const result = await this.articleService.getArticles(page, limit);
|
|
@@ -146,7 +146,7 @@ class ArticleController {
|
|
|
146
146
|
responseType: 'json' as const,
|
|
147
147
|
handler: async (params: any, ctx: any) => {
|
|
148
148
|
try {
|
|
149
|
-
const { title, content, author } = params
|
|
149
|
+
const { title, content, author } = params;
|
|
150
150
|
|
|
151
151
|
if (!title || !content) {
|
|
152
152
|
return {
|
|
@@ -183,7 +183,7 @@ class ArticleController {
|
|
|
183
183
|
handler: async (params: any, ctx: any) => {
|
|
184
184
|
try {
|
|
185
185
|
const id = parseInt(params.id);
|
|
186
|
-
const article = await this.articleService.updateArticle(id, params
|
|
186
|
+
const article = await this.articleService.updateArticle(id, params);
|
|
187
187
|
|
|
188
188
|
return {
|
|
189
189
|
success: true,
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"pack": "npx @linyjs/cli pack"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@linyjs/server-module-interface": "
|
|
13
|
-
"@linyjs/client-module-interface": "
|
|
12
|
+
"@linyjs/server-module-interface": "latest",
|
|
13
|
+
"@linyjs/client-module-interface": "latest",
|
|
14
14
|
"react": "^19.0.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linyjs/plugin-docs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Plugin development documentation and examples for linyjs framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"linyjs",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"build": "tsc"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@linyjs/client-module-interface": "
|
|
35
|
-
"@linyjs/server-module-interface": "
|
|
36
|
-
"@linyjs/ui-slots-interface": "
|
|
34
|
+
"@linyjs/client-module-interface": "latest",
|
|
35
|
+
"@linyjs/server-module-interface": "latest",
|
|
36
|
+
"@linyjs/ui-slots-interface": "latest"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"commander": "^11.1.0"
|