@linyjs/plugin-docs 0.1.3 → 0.1.4
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 +3 -8
- package/docs/api-reference/index.md +1 -1
- package/docs/best-practices/error-handling.md +1 -1
- package/docs/best-practices/naming-conventions.md +2 -1
- package/docs/best-practices/performance.md +4 -4
- package/docs/best-practices/service-definition.md +332 -0
- package/docs/faq.md +0 -1
- package/docs/getting-started/first-plugin.md +15 -15
- package/docs/getting-started/installation.md +1 -1
- package/docs/getting-started/quick-start.md +28 -22
- package/docs/guides/authentication.md +32 -24
- package/docs/guides/client-module.md +6 -0
- package/docs/guides/data-persistence.md +104 -43
- package/docs/guides/server-module.md +6 -0
- package/docs/migration/v0-to-v1.md +2 -2
- package/examples/analytics-plugin/src/client/index.tsx +43 -39
- package/examples/analytics-plugin/src/server/index.ts +20 -19
- package/examples/blog-plugin/README.md +4 -4
- package/examples/blog-plugin/package.json +1 -1
- package/examples/blog-plugin/src/client/index.tsx +44 -40
- package/examples/blog-plugin/src/server/index.ts +8 -8
- package/examples/hello-world/src/client/index.tsx +8 -8
- package/examples/hello-world/src/server/index.ts +8 -8
- package/package.json +10 -6
- package/src/cli.ts +12 -12
|
@@ -16,34 +16,89 @@ linyjs 提供 MongoDB 服务,支持:
|
|
|
16
16
|
### 在服务端模块中注入依赖
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
import type { IModule } from '@linyjs/server-module-interface';
|
|
19
|
+
import type { IModule, IServiceDef } from '@linyjs/server-module-interface';
|
|
20
|
+
import type { IMongoDBService } from '@linyjs/mdb';
|
|
21
|
+
|
|
22
|
+
class ArticleService {
|
|
23
|
+
constructor({ mongoDBService }: { mongoDBService: IMongoDBService }) {
|
|
24
|
+
this.mongoDBService = mongoDBService;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ... 服务方法
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const articleServiceDef: IServiceDef = {
|
|
31
|
+
serviceTag: 'articleService',
|
|
32
|
+
serviceImpl: ArticleService,
|
|
33
|
+
dependencies: ['mdb.mongoDBService'], // 注入 MongoDB 服务
|
|
34
|
+
meta: { usageType: 'normal_service' }
|
|
35
|
+
};
|
|
20
36
|
|
|
21
37
|
export const myServerModule: IModule = {
|
|
22
38
|
name: 'myServer',
|
|
23
|
-
moduleDependencies: ['
|
|
24
|
-
serviceDefs: [
|
|
25
|
-
{
|
|
26
|
-
serviceTag: 'articleService',
|
|
27
|
-
serviceImpl: ArticleService,
|
|
28
|
-
dependencies: ['mongodb.mongodb'], // 注入 MongoDB 服务
|
|
29
|
-
meta: { usageType: 'normal_service' }
|
|
30
|
-
}
|
|
31
|
-
]
|
|
39
|
+
moduleDependencies: ['mdb'], // 依赖 MongoDB 模块
|
|
40
|
+
serviceDefs: [articleServiceDef]
|
|
32
41
|
};
|
|
33
42
|
```
|
|
34
43
|
|
|
44
|
+
## 使用 ObjectId
|
|
45
|
+
|
|
46
|
+
在 MongoDB 中,文档的主键 `_id` 默认是 `ObjectId` 类型。在查询、更新、删除等操作中经常需要用 `ObjectId` 构造器将字符串 ID 转换为 `ObjectId` 实例。
|
|
47
|
+
|
|
48
|
+
### 为什么不从 mongodb 包直接导入?
|
|
49
|
+
|
|
50
|
+
linyjs 插件通常作为独立的 npm 包或 git 仓库开发,如果每个插件都直接依赖 `mongodb` npm 包,会带来以下问题:
|
|
51
|
+
|
|
52
|
+
- 📦 **包体积增大** — 插件单独安装一份 mongodb 驱动
|
|
53
|
+
- ⚠️ **版本冲突** — 插件与框架使用的 mongodb 版本不一致
|
|
54
|
+
- 🔧 **配置冗余** — 每个插件都需要在 `package.json` 中声明 mongodb 依赖
|
|
55
|
+
|
|
56
|
+
### 方式一:通过 IMongoDBService 使用
|
|
57
|
+
|
|
58
|
+
`@linyjs/mdb` 在 `IMongoDBService` 接口上暴露了 `ObjectId` 构造器,插件可以通过注入的 `mongoDBService` 直接使用:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import type { IMongoDBService } from '@linyjs/mdb';
|
|
62
|
+
|
|
63
|
+
class ArticleService {
|
|
64
|
+
constructor(private mongoDBService: IMongoDBService) {}
|
|
65
|
+
|
|
66
|
+
async getArticleById(id: string) {
|
|
67
|
+
const collection = this.mongoDBService.getCollection('articles');
|
|
68
|
+
// ✅ 通过 mongoDBService.ObjectId 使用,无需额外依赖
|
|
69
|
+
const article = await collection.findOne({
|
|
70
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
71
|
+
});
|
|
72
|
+
return article;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 方式二:从 @linyjs/mdb 直接导入
|
|
78
|
+
|
|
79
|
+
如果需要在服务类外部使用 `ObjectId`(如类型声明、工具函数等),也可以直接从 `@linyjs/mdb` 导入:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { ObjectId } from '@linyjs/mdb';
|
|
83
|
+
|
|
84
|
+
// 在路由 handler 或工具函数中使用
|
|
85
|
+
const articleId = new ObjectId('507f1f77bcf86cd799439011');
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> 💡 **推荐**:在服务类内部使用方式一(`this.mongoDBService.ObjectId`),避免额外的 import;在服务类外部使用方式二(`import { ObjectId } from '@linyjs/mdb'`)。两种方式都无需在插件中安装 `mongodb` 包。
|
|
89
|
+
|
|
35
90
|
## 基本 CRUD 操作
|
|
36
91
|
|
|
37
92
|
### 创建(Create)
|
|
38
93
|
|
|
39
94
|
```typescript
|
|
40
|
-
import type { IMongoDBService } from '@linyjs/
|
|
95
|
+
import type { IMongoDBService } from '@linyjs/mdb';
|
|
41
96
|
|
|
42
97
|
class ArticleService {
|
|
43
|
-
constructor(private
|
|
98
|
+
constructor(private mongodbService: IMongoDBService) {}
|
|
44
99
|
|
|
45
100
|
async createArticle(data: any) {
|
|
46
|
-
const db = this.
|
|
101
|
+
const db = this.mongodbService.getDb();
|
|
47
102
|
const collection = db.collection('articles');
|
|
48
103
|
|
|
49
104
|
const result = await collection.insertOne({
|
|
@@ -65,19 +120,25 @@ class ArticleService {
|
|
|
65
120
|
#### 获取单个文档
|
|
66
121
|
|
|
67
122
|
```typescript
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const article = await collection.findOne({
|
|
73
|
-
_id: new ObjectId(id)
|
|
74
|
-
});
|
|
123
|
+
import type { IMongoDBService } from '@linyjs/mdb';
|
|
124
|
+
|
|
125
|
+
class ArticleService {
|
|
126
|
+
constructor(private mongoDBService: IMongoDBService) {}
|
|
75
127
|
|
|
76
|
-
|
|
77
|
-
|
|
128
|
+
async getArticleById(id: string) {
|
|
129
|
+
const db = this.mongoDBService.getDb();
|
|
130
|
+
const collection = db.collection('articles');
|
|
131
|
+
|
|
132
|
+
const article = await collection.findOne({
|
|
133
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!article) {
|
|
137
|
+
throw new Error('Article not found');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return article;
|
|
78
141
|
}
|
|
79
|
-
|
|
80
|
-
return article;
|
|
81
142
|
}
|
|
82
143
|
```
|
|
83
144
|
|
|
@@ -85,7 +146,7 @@ async getArticleById(id: string) {
|
|
|
85
146
|
|
|
86
147
|
```typescript
|
|
87
148
|
async getArticles(page = 1, limit = 10) {
|
|
88
|
-
const db = this.
|
|
149
|
+
const db = this.mongodbService.getDb();
|
|
89
150
|
const collection = db.collection('articles');
|
|
90
151
|
|
|
91
152
|
const skip = (page - 1) * limit;
|
|
@@ -114,7 +175,7 @@ async getArticles(page = 1, limit = 10) {
|
|
|
114
175
|
|
|
115
176
|
```typescript
|
|
116
177
|
async getArticlesByAuthor(authorId: string) {
|
|
117
|
-
const db = this.
|
|
178
|
+
const db = this.mongodbService.getDb();
|
|
118
179
|
const collection = db.collection('articles');
|
|
119
180
|
|
|
120
181
|
const articles = await collection.find({
|
|
@@ -131,11 +192,11 @@ async getArticlesByAuthor(authorId: string) {
|
|
|
131
192
|
|
|
132
193
|
```typescript
|
|
133
194
|
async updateArticle(id: string, data: any) {
|
|
134
|
-
const db = this.
|
|
195
|
+
const db = this.mongodbService.getDb();
|
|
135
196
|
const collection = db.collection('articles');
|
|
136
197
|
|
|
137
198
|
const result = await collection.updateOne(
|
|
138
|
-
{ _id: new ObjectId(id) },
|
|
199
|
+
{ _id: new this.mongoDBService.ObjectId(id) },
|
|
139
200
|
{
|
|
140
201
|
$set: {
|
|
141
202
|
...data,
|
|
@@ -156,11 +217,11 @@ async updateArticle(id: string, data: any) {
|
|
|
156
217
|
|
|
157
218
|
```typescript
|
|
158
219
|
async updateArticleTitle(id: string, title: string) {
|
|
159
|
-
const db = this.
|
|
220
|
+
const db = this.mongodbService.getDb();
|
|
160
221
|
const collection = db.collection('articles');
|
|
161
222
|
|
|
162
223
|
await collection.updateOne(
|
|
163
|
-
{ _id: new ObjectId(id) },
|
|
224
|
+
{ _id: new this.mongoDBService.ObjectId(id) },
|
|
164
225
|
{
|
|
165
226
|
$set: {
|
|
166
227
|
title,
|
|
@@ -175,11 +236,11 @@ async updateArticleTitle(id: string, title: string) {
|
|
|
175
236
|
|
|
176
237
|
```typescript
|
|
177
238
|
async deleteArticle(id: string) {
|
|
178
|
-
const db = this.
|
|
239
|
+
const db = this.mongodbService.getDb();
|
|
179
240
|
const collection = db.collection('articles');
|
|
180
241
|
|
|
181
242
|
const result = await collection.deleteOne({
|
|
182
|
-
_id: new ObjectId(id)
|
|
243
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
183
244
|
});
|
|
184
245
|
|
|
185
246
|
if (result.deletedCount === 0) {
|
|
@@ -196,7 +257,7 @@ async deleteArticle(id: string) {
|
|
|
196
257
|
|
|
197
258
|
```typescript
|
|
198
259
|
async searchArticles(query: string, options: any) {
|
|
199
|
-
const db = this.
|
|
260
|
+
const db = this.mongodbService.getDb();
|
|
200
261
|
const collection = db.collection('articles');
|
|
201
262
|
|
|
202
263
|
// 构建查询条件
|
|
@@ -238,7 +299,7 @@ async searchArticles(query: string, options: any) {
|
|
|
238
299
|
|
|
239
300
|
```typescript
|
|
240
301
|
async getArticleStats() {
|
|
241
|
-
const db = this.
|
|
302
|
+
const db = this.mongodbService.getDb();
|
|
242
303
|
const collection = db.collection('articles');
|
|
243
304
|
|
|
244
305
|
const stats = await collection.aggregate([
|
|
@@ -269,7 +330,7 @@ async getArticleStats() {
|
|
|
269
330
|
|
|
270
331
|
```typescript
|
|
271
332
|
async getArticlesWithAuthors() {
|
|
272
|
-
const db = this.
|
|
333
|
+
const db = this.mongodbService.getDb();
|
|
273
334
|
const articlesCollection = db.collection('articles');
|
|
274
335
|
const usersCollection = db.collection('users');
|
|
275
336
|
|
|
@@ -309,7 +370,7 @@ async getArticlesWithAuthors() {
|
|
|
309
370
|
|
|
310
371
|
```typescript
|
|
311
372
|
async createIndexes() {
|
|
312
|
-
const db = this.
|
|
373
|
+
const db = this.mongodbService.getDb();
|
|
313
374
|
const collection = db.collection('articles');
|
|
314
375
|
|
|
315
376
|
// 单字段索引
|
|
@@ -334,7 +395,7 @@ async createIndexes() {
|
|
|
334
395
|
```typescript
|
|
335
396
|
// MongoDB 会自动使用合适的索引
|
|
336
397
|
async getArticlesByAuthor(authorId: string) {
|
|
337
|
-
const db = this.
|
|
398
|
+
const db = this.mongodbService.getDb();
|
|
338
399
|
const collection = db.collection('articles');
|
|
339
400
|
|
|
340
401
|
// 自动使用 authorId 索引
|
|
@@ -348,7 +409,7 @@ async getArticlesByAuthor(authorId: string) {
|
|
|
348
409
|
|
|
349
410
|
```typescript
|
|
350
411
|
async transferArticleAuthor(articleId: string, newAuthorId: string) {
|
|
351
|
-
const db = this.
|
|
412
|
+
const db = this.mongodbService.getDb();
|
|
352
413
|
const session = db.startSession();
|
|
353
414
|
|
|
354
415
|
try {
|
|
@@ -359,7 +420,7 @@ async transferArticleAuthor(articleId: string, newAuthorId: string) {
|
|
|
359
420
|
|
|
360
421
|
// 检查新作者是否存在
|
|
361
422
|
const user = await usersCollection.findOne(
|
|
362
|
-
{ _id: new ObjectId(newAuthorId) },
|
|
423
|
+
{ _id: new this.mongoDBService.ObjectId(newAuthorId) },
|
|
363
424
|
{ session }
|
|
364
425
|
);
|
|
365
426
|
|
|
@@ -369,7 +430,7 @@ async transferArticleAuthor(articleId: string, newAuthorId: string) {
|
|
|
369
430
|
|
|
370
431
|
// 更新文章作者
|
|
371
432
|
await articlesCollection.updateOne(
|
|
372
|
-
{ _id: new ObjectId(articleId) },
|
|
433
|
+
{ _id: new this.mongoDBService.ObjectId(articleId) },
|
|
373
434
|
{ $set: { authorId: newAuthorId } },
|
|
374
435
|
{ session }
|
|
375
436
|
);
|
|
@@ -409,7 +470,7 @@ async createArticle(data: any) {
|
|
|
409
470
|
throw new Error('Content too long');
|
|
410
471
|
}
|
|
411
472
|
|
|
412
|
-
const db = this.
|
|
473
|
+
const db = this.mongodbService.getDb();
|
|
413
474
|
const collection = db.collection('articles');
|
|
414
475
|
|
|
415
476
|
return await collection.insertOne({
|
|
@@ -430,7 +491,7 @@ async createArticle(data: any) {
|
|
|
430
491
|
```typescript
|
|
431
492
|
async safeOperation() {
|
|
432
493
|
try {
|
|
433
|
-
const db = this.
|
|
494
|
+
const db = this.mongodbService.getDb();
|
|
434
495
|
const collection = db.collection('articles');
|
|
435
496
|
|
|
436
497
|
const result = await collection.insertOne({ /* ... */ });
|
|
@@ -482,7 +543,7 @@ async getArticlesWithAuthors() {
|
|
|
482
543
|
|
|
483
544
|
```typescript
|
|
484
545
|
async getArticlesPaginated(page: number, limit: number) {
|
|
485
|
-
const db = this.
|
|
546
|
+
const db = this.mongodbService.getDb();
|
|
486
547
|
const collection = db.collection('articles');
|
|
487
548
|
|
|
488
549
|
// 使用游标分页(更高效)
|
|
@@ -302,6 +302,12 @@ handler: async (params, ctx) => {
|
|
|
302
302
|
|
|
303
303
|
## 最佳实践
|
|
304
304
|
|
|
305
|
+
### 0. 遵循 ServiceDef 代码风格规范
|
|
306
|
+
|
|
307
|
+
> 📐 **重要**: 请阅读 [ServiceDef 代码风格规范](../best-practices/service-definition.md),了解两条核心规则:
|
|
308
|
+
> 1. ServiceDef 必须声明为独立变量,禁止内联在 `serviceDefs` 数组中
|
|
309
|
+
> 2. ServiceDef 声明必须与实现类放在同一个文件中
|
|
310
|
+
|
|
305
311
|
### 1. 分离关注点
|
|
306
312
|
|
|
307
313
|
```
|
|
@@ -173,12 +173,12 @@ checkPermission({
|
|
|
173
173
|
|
|
174
174
|
#### 旧版本
|
|
175
175
|
```typescript
|
|
176
|
-
import { MongoConnector } from '@linyjs/
|
|
176
|
+
import { MongoConnector } from '@linyjs/mdb';
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
#### 新版本
|
|
180
180
|
```typescript
|
|
181
|
-
import { MongoService } from '@linyjs/
|
|
181
|
+
import { MongoService } from '@linyjs/mdb';
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
## 🖥️ 前端迁移
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { IModule } from '@linyjs/client-module-interface';
|
|
2
|
+
import type { IModule, IServiceDef } from '@linyjs/client-module-interface';
|
|
3
3
|
|
|
4
4
|
const API_BASE = '/api/analytics';
|
|
5
5
|
|
|
@@ -356,49 +356,53 @@ function AnalyticsTracker({ eventName, userId, children }: {
|
|
|
356
356
|
);
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
class AnalyticsRouteService {
|
|
360
|
+
basePath = '/analytics';
|
|
361
|
+
|
|
362
|
+
getRoutes() {
|
|
363
|
+
return [
|
|
364
|
+
{
|
|
365
|
+
path: '/',
|
|
366
|
+
component: AnalyticsDashboard,
|
|
367
|
+
key: 'analytics-dashboard',
|
|
368
|
+
meta: {
|
|
369
|
+
title: '分析仪表盘 - linyjs分析插件',
|
|
370
|
+
description: '查看应用使用统计和用户行为分析'
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
class AnalyticsComponentService {
|
|
378
|
+
getComponents() {
|
|
379
|
+
return {
|
|
380
|
+
AnalyticsDashboard,
|
|
381
|
+
AnalyticsTracker,
|
|
382
|
+
StatsCard,
|
|
383
|
+
SimpleChart
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const analyticsRouteDef: IServiceDef = {
|
|
389
|
+
serviceTag: 'analyticsRoute',
|
|
390
|
+
serviceImpl: AnalyticsRouteService,
|
|
391
|
+
meta: { usageType: 'route' }
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const analyticsComponentDef: IServiceDef = {
|
|
395
|
+
serviceTag: 'analyticsComponent',
|
|
396
|
+
serviceImpl: AnalyticsComponentService,
|
|
397
|
+
meta: { usageType: 'component' }
|
|
398
|
+
};
|
|
399
|
+
|
|
359
400
|
/**
|
|
360
401
|
* 客户端模块
|
|
361
402
|
*/
|
|
362
403
|
export const analyticsClientModule: IModule = {
|
|
363
404
|
name: 'analyticsClient',
|
|
364
|
-
serviceDefs: [
|
|
365
|
-
{
|
|
366
|
-
serviceTag: 'analyticsRoute',
|
|
367
|
-
serviceImpl: class AnalyticsRouteService {
|
|
368
|
-
basePath = '/analytics';
|
|
369
|
-
|
|
370
|
-
getRoutes() {
|
|
371
|
-
return [
|
|
372
|
-
{
|
|
373
|
-
path: '/',
|
|
374
|
-
component: AnalyticsDashboard,
|
|
375
|
-
key: 'analytics-dashboard',
|
|
376
|
-
meta: {
|
|
377
|
-
title: '分析仪表盘 - linyjs分析插件',
|
|
378
|
-
description: '查看应用使用统计和用户行为分析'
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
];
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
meta: { usageType: 'route' }
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
{
|
|
388
|
-
serviceTag: 'analyticsComponent',
|
|
389
|
-
serviceImpl: class AnalyticsComponentService {
|
|
390
|
-
getComponents() {
|
|
391
|
-
return {
|
|
392
|
-
AnalyticsDashboard,
|
|
393
|
-
AnalyticsTracker,
|
|
394
|
-
StatsCard,
|
|
395
|
-
SimpleChart
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
meta: { usageType: 'component' }
|
|
400
|
-
}
|
|
401
|
-
]
|
|
405
|
+
serviceDefs: [analyticsRouteDef, analyticsComponentDef]
|
|
402
406
|
};
|
|
403
407
|
|
|
404
408
|
// 导出组件
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IModule } from '@linyjs/server-module-interface';
|
|
1
|
+
import type { IModule, IServiceDef } from '@linyjs/server-module-interface';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 分析数据服务 - 收集和聚合应用使用数据
|
|
@@ -315,29 +315,30 @@ class AnalyticsCronService {
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
const analyticsControllerDef: IServiceDef = {
|
|
319
|
+
serviceTag: 'analyticsController',
|
|
320
|
+
serviceImpl: AnalyticsController,
|
|
321
|
+
meta: { usageType: 'controller' }
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const analyticsCronDef: IServiceDef = {
|
|
325
|
+
serviceTag: 'analyticsCron',
|
|
326
|
+
serviceImpl: AnalyticsCronService,
|
|
327
|
+
meta: {
|
|
328
|
+
usageType: 'service',
|
|
329
|
+
lifecycle: {
|
|
330
|
+
init: 'onModuleInit',
|
|
331
|
+
destroy: 'onModuleDestroy'
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
318
336
|
/**
|
|
319
337
|
* 服务端模块
|
|
320
338
|
*/
|
|
321
339
|
export const analyticsServerModule: IModule = {
|
|
322
340
|
name: 'analyticsServer',
|
|
323
|
-
serviceDefs: [
|
|
324
|
-
{
|
|
325
|
-
serviceTag: 'analyticsController',
|
|
326
|
-
serviceImpl: AnalyticsController,
|
|
327
|
-
meta: { usageType: 'controller' }
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
serviceTag: 'analyticsCron',
|
|
331
|
-
serviceImpl: AnalyticsCronService,
|
|
332
|
-
meta: {
|
|
333
|
-
usageType: 'service',
|
|
334
|
-
lifecycle: {
|
|
335
|
-
init: 'onModuleInit',
|
|
336
|
-
destroy: 'onModuleDestroy'
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
]
|
|
341
|
+
serviceDefs: [analyticsControllerDef, analyticsCronDef]
|
|
341
342
|
};
|
|
342
343
|
|
|
343
344
|
// 导出服务类供其他插件使用
|
|
@@ -153,7 +153,7 @@ Authorization: Bearer <token>
|
|
|
153
153
|
// src/server/index.ts
|
|
154
154
|
export const blogServerModule: IModule = {
|
|
155
155
|
name: 'blogServer',
|
|
156
|
-
moduleDependencies: ['
|
|
156
|
+
moduleDependencies: ['mdb', 'auth'],
|
|
157
157
|
serviceDefs: [
|
|
158
158
|
articleControllerDef,
|
|
159
159
|
articleServiceDef
|
|
@@ -188,12 +188,12 @@ export const blogClientModule: IModule = {
|
|
|
188
188
|
// src/server/services/article-service.ts
|
|
189
189
|
class ArticleService {
|
|
190
190
|
constructor(
|
|
191
|
-
private
|
|
191
|
+
private mongodbService: IMongoDBService,
|
|
192
192
|
private auth: IAuthService
|
|
193
193
|
) {}
|
|
194
194
|
|
|
195
195
|
async createArticle(data: CreateArticleDto, userId: string) {
|
|
196
|
-
const db = this.
|
|
196
|
+
const db = this.mongodbService.getDb();
|
|
197
197
|
const collection = db.collection('articles');
|
|
198
198
|
|
|
199
199
|
const article = {
|
|
@@ -211,7 +211,7 @@ class ArticleService {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
async getArticles(filters: ArticleFilters = {}) {
|
|
214
|
-
const db = this.
|
|
214
|
+
const db = this.mongodbService.getDb();
|
|
215
215
|
const collection = db.collection('articles');
|
|
216
216
|
|
|
217
217
|
const query: any = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { IModule } from '@linyjs/client-module-interface';
|
|
2
|
+
import type { IModule, IServiceDef } from '@linyjs/client-module-interface';
|
|
3
3
|
|
|
4
4
|
const API_BASE = '/api/articles';
|
|
5
5
|
|
|
@@ -350,50 +350,54 @@ function formatContent(content: string): string {
|
|
|
350
350
|
.replace(/`(.*?)`/g, '<code>$1</code>');
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
class BlogRouteService {
|
|
354
|
+
basePath = '/blog';
|
|
355
|
+
|
|
356
|
+
getRoutes() {
|
|
357
|
+
return [
|
|
358
|
+
{
|
|
359
|
+
path: '/',
|
|
360
|
+
component: BlogHome,
|
|
361
|
+
key: 'blog-home',
|
|
362
|
+
meta: {
|
|
363
|
+
title: '博客首页 - linyjs示例',
|
|
364
|
+
description: '一个简单的博客示例,展示linyjs插件开发'
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
class BlogComponentService {
|
|
372
|
+
// 注册全局组件
|
|
373
|
+
getComponents() {
|
|
374
|
+
return {
|
|
375
|
+
BlogHome,
|
|
376
|
+
ArticleList,
|
|
377
|
+
ArticleDetail,
|
|
378
|
+
ArticleForm
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const blogRouteDef: IServiceDef = {
|
|
384
|
+
serviceTag: 'blogRoute',
|
|
385
|
+
serviceImpl: BlogRouteService,
|
|
386
|
+
meta: { usageType: 'route' }
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const blogComponentDef: IServiceDef = {
|
|
390
|
+
serviceTag: 'blogComponent',
|
|
391
|
+
serviceImpl: BlogComponentService,
|
|
392
|
+
meta: { usageType: 'component' }
|
|
393
|
+
};
|
|
394
|
+
|
|
353
395
|
/**
|
|
354
396
|
* 客户端模块定义
|
|
355
397
|
*/
|
|
356
398
|
export const blogClientModule: IModule = {
|
|
357
399
|
name: 'blogClient',
|
|
358
|
-
serviceDefs: [
|
|
359
|
-
{
|
|
360
|
-
serviceTag: 'blogRoute',
|
|
361
|
-
serviceImpl: class BlogRouteService {
|
|
362
|
-
basePath = '/blog';
|
|
363
|
-
|
|
364
|
-
getRoutes() {
|
|
365
|
-
return [
|
|
366
|
-
{
|
|
367
|
-
path: '/',
|
|
368
|
-
component: BlogHome,
|
|
369
|
-
key: 'blog-home',
|
|
370
|
-
meta: {
|
|
371
|
-
title: '博客首页 - linyjs示例',
|
|
372
|
-
description: '一个简单的博客示例,展示linyjs插件开发'
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
];
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
meta: { usageType: 'route' }
|
|
379
|
-
},
|
|
380
|
-
|
|
381
|
-
{
|
|
382
|
-
serviceTag: 'blogComponent',
|
|
383
|
-
serviceImpl: class BlogComponentService {
|
|
384
|
-
// 注册全局组件
|
|
385
|
-
getComponents() {
|
|
386
|
-
return {
|
|
387
|
-
BlogHome,
|
|
388
|
-
ArticleList,
|
|
389
|
-
ArticleDetail,
|
|
390
|
-
ArticleForm
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
meta: { usageType: 'component' }
|
|
395
|
-
}
|
|
396
|
-
]
|
|
400
|
+
serviceDefs: [blogRouteDef, blogComponentDef]
|
|
397
401
|
};
|
|
398
402
|
|
|
399
403
|
// 导出组件方便其他模块使用
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IModule } from '@linyjs/server-module-interface';
|
|
1
|
+
import type { IModule, IServiceDef } from '@linyjs/server-module-interface';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 文章服务 - 处理文章的业务逻辑
|
|
@@ -225,16 +225,16 @@ class ArticleController {
|
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
const articleControllerDef: IServiceDef = {
|
|
229
|
+
serviceTag: 'articleController',
|
|
230
|
+
serviceImpl: ArticleController,
|
|
231
|
+
meta: { usageType: 'controller' }
|
|
232
|
+
};
|
|
233
|
+
|
|
228
234
|
/**
|
|
229
235
|
* 服务端模块定义
|
|
230
236
|
*/
|
|
231
237
|
export const blogServerModule: IModule = {
|
|
232
238
|
name: 'blogServer',
|
|
233
|
-
serviceDefs: [
|
|
234
|
-
{
|
|
235
|
-
serviceTag: 'articleController',
|
|
236
|
-
serviceImpl: ArticleController,
|
|
237
|
-
meta: { usageType: 'controller' }
|
|
238
|
-
}
|
|
239
|
-
]
|
|
239
|
+
serviceDefs: [articleControllerDef]
|
|
240
240
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IModule } from '@linyjs/client-module-interface';
|
|
1
|
+
import type { IModule, IServiceDef } from '@linyjs/client-module-interface';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -95,16 +95,16 @@ class HelloRouteService {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
const helloRouteDef: IServiceDef = {
|
|
99
|
+
serviceTag: 'helloRoute',
|
|
100
|
+
serviceImpl: HelloRouteService,
|
|
101
|
+
meta: { usageType: 'route' }
|
|
102
|
+
};
|
|
103
|
+
|
|
98
104
|
/**
|
|
99
105
|
* 客户端模块定义
|
|
100
106
|
*/
|
|
101
107
|
export const helloClientModule: IModule = {
|
|
102
108
|
name: 'helloClient',
|
|
103
|
-
serviceDefs: [
|
|
104
|
-
{
|
|
105
|
-
serviceTag: 'helloRoute',
|
|
106
|
-
serviceImpl: HelloRouteService,
|
|
107
|
-
meta: { usageType: 'route' }
|
|
108
|
-
}
|
|
109
|
-
]
|
|
109
|
+
serviceDefs: [helloRouteDef]
|
|
110
110
|
};
|