@linyjs/plugin-docs 0.0.13
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 +137 -0
- package/docs/api-reference/index.md +361 -0
- package/docs/best-practices/error-handling.md +462 -0
- package/docs/best-practices/naming-conventions.md +496 -0
- package/docs/best-practices/performance.md +532 -0
- package/docs/best-practices/security.md +620 -0
- package/docs/best-practices/service-definition.md +332 -0
- package/docs/faq.md +322 -0
- package/docs/getting-started/first-plugin.md +358 -0
- package/docs/getting-started/installation.md +244 -0
- package/docs/getting-started/quick-start.md +235 -0
- package/docs/guides/authentication.md +428 -0
- package/docs/guides/client-module.md +604 -0
- package/docs/guides/data-persistence.md +640 -0
- package/docs/guides/permissions.md +440 -0
- package/docs/guides/server-module.md +377 -0
- package/docs/guides/ui-slots.md +475 -0
- package/docs/migration/v0-to-v1.md +354 -0
- package/examples/analytics-plugin/README.md +416 -0
- package/examples/analytics-plugin/dist/client/index.d.ts +36 -0
- package/examples/analytics-plugin/dist/client/index.js +173 -0
- package/examples/analytics-plugin/dist/index.d.ts +64 -0
- package/examples/analytics-plugin/dist/index.js +72 -0
- package/examples/analytics-plugin/dist/server/index.d.ts +119 -0
- package/examples/analytics-plugin/dist/server/index.js +313 -0
- package/examples/analytics-plugin/package.json +28 -0
- package/examples/analytics-plugin/src/client/index.tsx +414 -0
- package/examples/analytics-plugin/src/index.ts +71 -0
- package/examples/analytics-plugin/src/server/index.ts +349 -0
- package/examples/analytics-plugin/tsconfig.json +21 -0
- package/examples/blog-plugin/README.md +319 -0
- package/examples/blog-plugin/dist/client/index.d.ts +22 -0
- package/examples/blog-plugin/dist/client/index.js +197 -0
- package/examples/blog-plugin/dist/server/index.d.ts +5 -0
- package/examples/blog-plugin/dist/server/index.js +220 -0
- package/examples/blog-plugin/package.json +24 -0
- package/examples/blog-plugin/src/client/index.tsx +409 -0
- package/examples/blog-plugin/src/server/index.ts +240 -0
- package/examples/blog-plugin/tsconfig.json +16 -0
- package/examples/hello-world/README.md +101 -0
- package/examples/hello-world/dist/client/index.d.ts +5 -0
- package/examples/hello-world/dist/client/index.js +80 -0
- package/examples/hello-world/dist/server/index.d.ts +5 -0
- package/examples/hello-world/dist/server/index.js +38 -0
- package/examples/hello-world/node_modules/.package-lock.json +61 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/HOOKS_USAGE.md +281 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/IMPLEMENTATION_SUMMARY.md +166 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/QUICK_REFERENCE.md +130 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/README.md +214 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/package.json +26 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/application.ts +50 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/comparison-example.tsx +229 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/di.ts +127 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/domain.ts +52 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/hooks-example.tsx +275 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/hooks.ts +175 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/index.ts +87 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/interfaces.ts +117 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/router.ts +49 -0
- package/examples/hello-world/node_modules/@linyjs/client-module-interface/tsconfig.json +10 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/package.json +21 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/application.ts +21 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/di.ts +127 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/domain.ts +75 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/index.ts +55 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/interfaces.ts +58 -0
- package/examples/hello-world/node_modules/@linyjs/server-module-interface/tsconfig.json +8 -0
- package/examples/hello-world/node_modules/@types/react/LICENSE +21 -0
- package/examples/hello-world/node_modules/@types/react/README.md +15 -0
- package/examples/hello-world/node_modules/@types/react/canary.d.ts +129 -0
- package/examples/hello-world/node_modules/@types/react/compiler-runtime.d.ts +4 -0
- package/examples/hello-world/node_modules/@types/react/experimental.d.ts +184 -0
- package/examples/hello-world/node_modules/@types/react/global.d.ts +166 -0
- package/examples/hello-world/node_modules/@types/react/index.d.ts +4369 -0
- package/examples/hello-world/node_modules/@types/react/jsx-dev-runtime.d.ts +33 -0
- package/examples/hello-world/node_modules/@types/react/jsx-runtime.d.ts +24 -0
- package/examples/hello-world/node_modules/@types/react/package.json +210 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/canary.d.ts +129 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/experimental.d.ts +184 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/global.d.ts +166 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/index.d.ts +4356 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/jsx-dev-runtime.d.ts +33 -0
- package/examples/hello-world/node_modules/@types/react/ts5.0/jsx-runtime.d.ts +24 -0
- package/examples/hello-world/node_modules/csstype/LICENSE +19 -0
- package/examples/hello-world/node_modules/csstype/README.md +291 -0
- package/examples/hello-world/node_modules/csstype/index.d.ts +22569 -0
- package/examples/hello-world/node_modules/csstype/index.js.flow +6863 -0
- package/examples/hello-world/node_modules/csstype/package.json +70 -0
- package/examples/hello-world/node_modules/react/LICENSE +21 -0
- package/examples/hello-world/node_modules/react/README.md +37 -0
- package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.development.js +24 -0
- package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.production.js +16 -0
- package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.profiling.js +16 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.development.js +338 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.production.js +14 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.profiling.js +14 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.react-server.development.js +370 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.react-server.production.js +40 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.development.js +352 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.production.js +34 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.profiling.js +34 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.react-server.development.js +370 -0
- package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.react-server.production.js +40 -0
- package/examples/hello-world/node_modules/react/cjs/react.development.js +1284 -0
- package/examples/hello-world/node_modules/react/cjs/react.production.js +542 -0
- package/examples/hello-world/node_modules/react/cjs/react.react-server.development.js +848 -0
- package/examples/hello-world/node_modules/react/cjs/react.react-server.production.js +423 -0
- package/examples/hello-world/node_modules/react/compiler-runtime.js +14 -0
- package/examples/hello-world/node_modules/react/index.js +7 -0
- package/examples/hello-world/node_modules/react/jsx-dev-runtime.js +7 -0
- package/examples/hello-world/node_modules/react/jsx-dev-runtime.react-server.js +7 -0
- package/examples/hello-world/node_modules/react/jsx-runtime.js +7 -0
- package/examples/hello-world/node_modules/react/jsx-runtime.react-server.js +7 -0
- package/examples/hello-world/node_modules/react/package.json +51 -0
- package/examples/hello-world/node_modules/react/react.react-server.js +7 -0
- package/examples/hello-world/node_modules/typescript/LICENSE.txt +55 -0
- package/examples/hello-world/node_modules/typescript/README.md +50 -0
- package/examples/hello-world/node_modules/typescript/SECURITY.md +41 -0
- package/examples/hello-world/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
- package/examples/hello-world/node_modules/typescript/bin/tsc +2 -0
- package/examples/hello-world/node_modules/typescript/bin/tsserver +2 -0
- package/examples/hello-world/node_modules/typescript/lib/_tsc.js +133818 -0
- package/examples/hello-world/node_modules/typescript/lib/_tsserver.js +659 -0
- package/examples/hello-world/node_modules/typescript/lib/_typingsInstaller.js +222 -0
- package/examples/hello-world/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.d.ts +22 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.es6.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
- package/examples/hello-world/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
- package/examples/hello-world/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/tsc.js +8 -0
- package/examples/hello-world/node_modules/typescript/lib/tsserver.js +8 -0
- package/examples/hello-world/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
- package/examples/hello-world/node_modules/typescript/lib/tsserverlibrary.js +21 -0
- package/examples/hello-world/node_modules/typescript/lib/typesMap.json +497 -0
- package/examples/hello-world/node_modules/typescript/lib/typescript.d.ts +11437 -0
- package/examples/hello-world/node_modules/typescript/lib/typescript.js +200276 -0
- package/examples/hello-world/node_modules/typescript/lib/typingsInstaller.js +8 -0
- package/examples/hello-world/node_modules/typescript/lib/watchGuard.js +53 -0
- package/examples/hello-world/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
- package/examples/hello-world/node_modules/typescript/package.json +120 -0
- package/examples/hello-world/package-lock.json +74 -0
- package/examples/hello-world/package.json +23 -0
- package/examples/hello-world/src/client/index.tsx +110 -0
- package/examples/hello-world/src/server/index.ts +42 -0
- package/examples/hello-world/tsconfig.json +16 -0
- package/package.json +46 -0
- package/src/cli.ts +321 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
# 数据持久化
|
|
2
|
+
|
|
3
|
+
本指南介绍如何在 linyjs 插件中使用 MongoDB 进行数据持久化。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
linyjs 提供 MongoDB 服务,支持:
|
|
8
|
+
|
|
9
|
+
- 💾 CRUD 操作(创建、读取、更新、删除)
|
|
10
|
+
- 🔍 复杂查询和聚合
|
|
11
|
+
- 📊 索引优化
|
|
12
|
+
- 🔗 数据关联
|
|
13
|
+
|
|
14
|
+
## 配置 MongoDB 服务
|
|
15
|
+
|
|
16
|
+
### 在服务端模块中注入依赖
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import type { IModule, IServiceDef } from '@linyjs/server-module-interface';
|
|
20
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
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
|
+
};
|
|
36
|
+
|
|
37
|
+
export const myServerModule: IModule = {
|
|
38
|
+
name: 'myServer',
|
|
39
|
+
moduleDependencies: ['mdb'], // 依赖 MongoDB 模块
|
|
40
|
+
serviceDefs: [articleServiceDef]
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 使用 @linyjs/shared-module-interface 获取类型提示
|
|
45
|
+
|
|
46
|
+
linyjs 插件通常作为独立的 npm 包或 git 仓库开发。通过依赖注入机制,插件可以获取 `IMongoDBService` 等服务实例,但 TypeScript 需要类型声明才能提供完整的类型提示。
|
|
47
|
+
|
|
48
|
+
### 安装
|
|
49
|
+
|
|
50
|
+
在插件的 `package.json` 中添加 `@linyjs/shared-module-interface` 作为开发依赖:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@linyjs/shared-module-interface": "^0.0.1"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
此包是**纯类型包**,包含了所有公共模块(mdb、auth、permission)的服务接口和类型定义,并重新导出了 `mongodb` 包的全部类型(`ObjectId`、`Db`、`Collection`、`Document`、`Filter` 等),使插件无需单独安装 `mongodb`、`@linyjs/mdb`、`@linyjs/auth`、`@linyjs/permission` 等包。
|
|
61
|
+
|
|
62
|
+
> ⚠️ **纯类型包**:此包只导出 TypeScript 类型(`export type`),不包含任何运行时代码。无法通过 `import { ObjectId }` 导入运行时值,运行时请通过依赖注入获取的 `mongoDBService.ObjectId` 使用。
|
|
63
|
+
|
|
64
|
+
### 可用类型一览
|
|
65
|
+
|
|
66
|
+
| 类型 | 说明 | 来源模块 |
|
|
67
|
+
|------|------|----------|
|
|
68
|
+
| `IMongoDBService` | MongoDB 服务接口 | @linyjs/mdb |
|
|
69
|
+
| `ObjectId`, `Db`, `Collection`, `Document`, `Filter` ... | mongodb 全部类型 | mongodb |
|
|
70
|
+
| `IUserService` | 用户管理服务接口 | @linyjs/auth |
|
|
71
|
+
| `IAuthUser`, `ISafeUser`, `IAuthResult`, `IJwtPayload` | 认证相关类型 | @linyjs/auth |
|
|
72
|
+
| `IPermissionService` | 权限服务接口 | @linyjs/permission |
|
|
73
|
+
| `IPermissionPolicy`, `IPermissionCheckResult`, `Role` | 权限相关类型 | @linyjs/permission |
|
|
74
|
+
|
|
75
|
+
## 使用 ObjectId
|
|
76
|
+
|
|
77
|
+
在 MongoDB 中,文档的主键 `_id` 默认是 `ObjectId` 类型。在查询、更新、删除等操作中经常需要用 `ObjectId` 构造器将字符串 ID 转换为 `ObjectId` 实例。
|
|
78
|
+
|
|
79
|
+
### 为什么不从 mongodb 包直接导入?
|
|
80
|
+
|
|
81
|
+
如果每个插件都直接依赖 `mongodb` npm 包,会带来以下问题:
|
|
82
|
+
|
|
83
|
+
- 📦 **包体积增大** — 插件单独安装一份 mongodb 驱动
|
|
84
|
+
- ⚠️ **版本冲突** — 插件与框架使用的 mongodb 版本不一致
|
|
85
|
+
- 🔧 **配置冗余** — 每个插件都需要在 `package.json` 中声明 mongodb 依赖
|
|
86
|
+
|
|
87
|
+
### 通过 IMongoDBService 使用
|
|
88
|
+
|
|
89
|
+
`@linyjs/shared-module-interface` 是纯类型包,不导出运行时值。`ObjectId` 构造器通过 `IMongoDBService` 接口的 `ObjectId` 属性暴露,插件通过注入的 `mongoDBService` 在运行时获取:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
93
|
+
|
|
94
|
+
class ArticleService {
|
|
95
|
+
constructor(private mongoDBService: IMongoDBService) {}
|
|
96
|
+
|
|
97
|
+
async getArticleById(id: string) {
|
|
98
|
+
const collection = this.mongoDBService.getCollection('articles');
|
|
99
|
+
// ✅ 通过 mongoDBService.ObjectId 获取运行时构造器
|
|
100
|
+
const article = await collection.findOne({
|
|
101
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
102
|
+
});
|
|
103
|
+
return article;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
如果需要在服务类外部(如工具函数)创建 `ObjectId`,可以通过一个辅助函数转发:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// db.ts - 在模块内部保存 mongoDBService 引用
|
|
112
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
113
|
+
|
|
114
|
+
let mongoDBService: IMongoDBService | null = null;
|
|
115
|
+
|
|
116
|
+
export function setMongoDBService(service: IMongoDBService) {
|
|
117
|
+
mongoDBService = service;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** 通过注入的 MongoDB 服务创建 ObjectId */
|
|
121
|
+
export function createObjectId(id: string) {
|
|
122
|
+
if (!mongoDBService) throw new Error('MongoDB service not initialized');
|
|
123
|
+
return new mongoDBService.ObjectId(id);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 在 service.ts 中使用
|
|
127
|
+
import { createObjectId } from './db.js';
|
|
128
|
+
const objectId = createObjectId(userId);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> 💡 **类型使用**:如果仅需在类型注解中使用 `ObjectId`(如 `_id?: ObjectId`),可以用 `import type { ObjectId } from '@linyjs/shared-module-interface'`,这是纯类型导入,编译后会被完全擦除。
|
|
132
|
+
|
|
133
|
+
## 基本 CRUD 操作
|
|
134
|
+
|
|
135
|
+
### 创建(Create)
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
139
|
+
|
|
140
|
+
class ArticleService {
|
|
141
|
+
constructor(private mongodbService: IMongoDBService) {}
|
|
142
|
+
|
|
143
|
+
async createArticle(data: any) {
|
|
144
|
+
const db = this.mongodbService.getDb();
|
|
145
|
+
const collection = db.collection('articles');
|
|
146
|
+
|
|
147
|
+
const result = await collection.insertOne({
|
|
148
|
+
...data,
|
|
149
|
+
createdAt: new Date(),
|
|
150
|
+
updatedAt: new Date()
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
id: result.insertedId,
|
|
155
|
+
...data
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 读取(Read)
|
|
162
|
+
|
|
163
|
+
#### 获取单个文档
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import type { IMongoDBService } from '@linyjs/shared-module-interface';
|
|
167
|
+
|
|
168
|
+
class ArticleService {
|
|
169
|
+
constructor(private mongoDBService: IMongoDBService) {}
|
|
170
|
+
|
|
171
|
+
async getArticleById(id: string) {
|
|
172
|
+
const db = this.mongoDBService.getDb();
|
|
173
|
+
const collection = db.collection('articles');
|
|
174
|
+
|
|
175
|
+
const article = await collection.findOne({
|
|
176
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (!article) {
|
|
180
|
+
throw new Error('Article not found');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return article;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### 获取列表(支持分页)
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
async getArticles(page = 1, limit = 10) {
|
|
192
|
+
const db = this.mongodbService.getDb();
|
|
193
|
+
const collection = db.collection('articles');
|
|
194
|
+
|
|
195
|
+
const skip = (page - 1) * limit;
|
|
196
|
+
|
|
197
|
+
// 获取总数
|
|
198
|
+
const total = await collection.countDocuments();
|
|
199
|
+
|
|
200
|
+
// 获取数据
|
|
201
|
+
const articles = await collection.find()
|
|
202
|
+
.skip(skip)
|
|
203
|
+
.limit(limit)
|
|
204
|
+
.sort({ createdAt: -1 })
|
|
205
|
+
.toArray();
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
data: articles,
|
|
209
|
+
total,
|
|
210
|
+
page,
|
|
211
|
+
limit,
|
|
212
|
+
totalPages: Math.ceil(total / limit)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### 条件查询
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
async getArticlesByAuthor(authorId: string) {
|
|
221
|
+
const db = this.mongodbService.getDb();
|
|
222
|
+
const collection = db.collection('articles');
|
|
223
|
+
|
|
224
|
+
const articles = await collection.find({
|
|
225
|
+
authorId
|
|
226
|
+
}).toArray();
|
|
227
|
+
|
|
228
|
+
return articles;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 更新(Update)
|
|
233
|
+
|
|
234
|
+
#### 完整更新
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
async updateArticle(id: string, data: any) {
|
|
238
|
+
const db = this.mongodbService.getDb();
|
|
239
|
+
const collection = db.collection('articles');
|
|
240
|
+
|
|
241
|
+
const result = await collection.updateOne(
|
|
242
|
+
{ _id: new this.mongoDBService.ObjectId(id) },
|
|
243
|
+
{
|
|
244
|
+
$set: {
|
|
245
|
+
...data,
|
|
246
|
+
updatedAt: new Date()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (result.matchedCount === 0) {
|
|
252
|
+
throw new Error('Article not found');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { success: true };
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### 部分更新
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
async updateArticleTitle(id: string, title: string) {
|
|
263
|
+
const db = this.mongodbService.getDb();
|
|
264
|
+
const collection = db.collection('articles');
|
|
265
|
+
|
|
266
|
+
await collection.updateOne(
|
|
267
|
+
{ _id: new this.mongoDBService.ObjectId(id) },
|
|
268
|
+
{
|
|
269
|
+
$set: {
|
|
270
|
+
title,
|
|
271
|
+
updatedAt: new Date()
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 删除(Delete)
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
async deleteArticle(id: string) {
|
|
282
|
+
const db = this.mongodbService.getDb();
|
|
283
|
+
const collection = db.collection('articles');
|
|
284
|
+
|
|
285
|
+
const result = await collection.deleteOne({
|
|
286
|
+
_id: new this.mongoDBService.ObjectId(id)
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if (result.deletedCount === 0) {
|
|
290
|
+
throw new Error('Article not found');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return { success: true };
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## 高级查询
|
|
298
|
+
|
|
299
|
+
### 复杂条件
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
async searchArticles(query: string, options: any) {
|
|
303
|
+
const db = this.mongodbService.getDb();
|
|
304
|
+
const collection = db.collection('articles');
|
|
305
|
+
|
|
306
|
+
// 构建查询条件
|
|
307
|
+
const conditions: any = {};
|
|
308
|
+
|
|
309
|
+
if (query) {
|
|
310
|
+
conditions.$or = [
|
|
311
|
+
{ title: { $regex: query, $options: 'i' } },
|
|
312
|
+
{ content: { $regex: query, $options: 'i' } }
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (options.authorId) {
|
|
317
|
+
conditions.authorId = options.authorId;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (options.status) {
|
|
321
|
+
conditions.status = options.status;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 日期范围
|
|
325
|
+
if (options.startDate || options.endDate) {
|
|
326
|
+
conditions.createdAt = {};
|
|
327
|
+
if (options.startDate) {
|
|
328
|
+
conditions.createdAt.$gte = new Date(options.startDate);
|
|
329
|
+
}
|
|
330
|
+
if (options.endDate) {
|
|
331
|
+
conditions.createdAt.$lte = new Date(options.endDate);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const articles = await collection.find(conditions).toArray();
|
|
336
|
+
|
|
337
|
+
return articles;
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 聚合管道
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
async getArticleStats() {
|
|
345
|
+
const db = this.mongodbService.getDb();
|
|
346
|
+
const collection = db.collection('articles');
|
|
347
|
+
|
|
348
|
+
const stats = await collection.aggregate([
|
|
349
|
+
// 按作者分组
|
|
350
|
+
{
|
|
351
|
+
$group: {
|
|
352
|
+
_id: '$authorId',
|
|
353
|
+
articleCount: { $sum: 1 },
|
|
354
|
+
totalViews: { $sum: '$views' },
|
|
355
|
+
avgViews: { $avg: '$views' }
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
// 排序
|
|
359
|
+
{
|
|
360
|
+
$sort: { articleCount: -1 }
|
|
361
|
+
},
|
|
362
|
+
// 限制结果数
|
|
363
|
+
{
|
|
364
|
+
$limit: 10
|
|
365
|
+
}
|
|
366
|
+
]).toArray();
|
|
367
|
+
|
|
368
|
+
return stats;
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### 联表查询
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
async getArticlesWithAuthors() {
|
|
376
|
+
const db = this.mongodbService.getDb();
|
|
377
|
+
const articlesCollection = db.collection('articles');
|
|
378
|
+
const usersCollection = db.collection('users');
|
|
379
|
+
|
|
380
|
+
const articles = await articlesCollection.aggregate([
|
|
381
|
+
// 联查用户表
|
|
382
|
+
{
|
|
383
|
+
$lookup: {
|
|
384
|
+
from: 'users',
|
|
385
|
+
localField: 'authorId',
|
|
386
|
+
foreignField: '_id',
|
|
387
|
+
as: 'author'
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
// 展开数组
|
|
391
|
+
{
|
|
392
|
+
$unwind: '$author'
|
|
393
|
+
},
|
|
394
|
+
// 投影字段
|
|
395
|
+
{
|
|
396
|
+
$project: {
|
|
397
|
+
title: 1,
|
|
398
|
+
content: 1,
|
|
399
|
+
createdAt: 1,
|
|
400
|
+
'author.name': 1,
|
|
401
|
+
'author.email': 1
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
]).toArray();
|
|
405
|
+
|
|
406
|
+
return articles;
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## 索引优化
|
|
411
|
+
|
|
412
|
+
### 创建索引
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
async createIndexes() {
|
|
416
|
+
const db = this.mongodbService.getDb();
|
|
417
|
+
const collection = db.collection('articles');
|
|
418
|
+
|
|
419
|
+
// 单字段索引
|
|
420
|
+
await collection.createIndex({ authorId: 1 });
|
|
421
|
+
|
|
422
|
+
// 复合索引
|
|
423
|
+
await collection.createIndex({ authorId: 1, createdAt: -1 });
|
|
424
|
+
|
|
425
|
+
// 文本索引(用于全文搜索)
|
|
426
|
+
await collection.createIndex({
|
|
427
|
+
title: 'text',
|
|
428
|
+
content: 'text'
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// 唯一索引
|
|
432
|
+
await collection.createIndex({ slug: 1 }, { unique: true });
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### 使用索引
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
// MongoDB 会自动使用合适的索引
|
|
440
|
+
async getArticlesByAuthor(authorId: string) {
|
|
441
|
+
const db = this.mongodbService.getDb();
|
|
442
|
+
const collection = db.collection('articles');
|
|
443
|
+
|
|
444
|
+
// 自动使用 authorId 索引
|
|
445
|
+
return await collection.find({ authorId }).toArray();
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## 事务处理
|
|
450
|
+
|
|
451
|
+
### 使用事务
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
async transferArticleAuthor(articleId: string, newAuthorId: string) {
|
|
455
|
+
const db = this.mongodbService.getDb();
|
|
456
|
+
const session = db.startSession();
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
session.startTransaction();
|
|
460
|
+
|
|
461
|
+
const articlesCollection = db.collection('articles');
|
|
462
|
+
const usersCollection = db.collection('users');
|
|
463
|
+
|
|
464
|
+
// 检查新作者是否存在
|
|
465
|
+
const user = await usersCollection.findOne(
|
|
466
|
+
{ _id: new this.mongoDBService.ObjectId(newAuthorId) },
|
|
467
|
+
{ session }
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (!user) {
|
|
471
|
+
throw new Error('User not found');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// 更新文章作者
|
|
475
|
+
await articlesCollection.updateOne(
|
|
476
|
+
{ _id: new this.mongoDBService.ObjectId(articleId) },
|
|
477
|
+
{ $set: { authorId: newAuthorId } },
|
|
478
|
+
{ session }
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// 提交事务
|
|
482
|
+
await session.commitTransaction();
|
|
483
|
+
|
|
484
|
+
return { success: true };
|
|
485
|
+
} catch (error) {
|
|
486
|
+
// 回滚事务
|
|
487
|
+
await session.abortTransaction();
|
|
488
|
+
throw error;
|
|
489
|
+
} finally {
|
|
490
|
+
session.endSession();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## 数据验证
|
|
496
|
+
|
|
497
|
+
### Schema 验证
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
async createArticle(data: any) {
|
|
501
|
+
// 验证必填字段
|
|
502
|
+
if (!data.title || !data.content) {
|
|
503
|
+
throw new Error('Title and content are required');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// 验证标题长度
|
|
507
|
+
if (data.title.length > 200) {
|
|
508
|
+
throw new Error('Title too long');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 验证内容长度
|
|
512
|
+
if (data.content.length > 50000) {
|
|
513
|
+
throw new Error('Content too long');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const db = this.mongodbService.getDb();
|
|
517
|
+
const collection = db.collection('articles');
|
|
518
|
+
|
|
519
|
+
return await collection.insertOne({
|
|
520
|
+
title: data.title.trim(),
|
|
521
|
+
content: data.content.trim(),
|
|
522
|
+
authorId: data.authorId,
|
|
523
|
+
status: 'draft',
|
|
524
|
+
createdAt: new Date(),
|
|
525
|
+
updatedAt: new Date()
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## 最佳实践
|
|
531
|
+
|
|
532
|
+
### 1. 错误处理
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
async safeOperation() {
|
|
536
|
+
try {
|
|
537
|
+
const db = this.mongodbService.getDb();
|
|
538
|
+
const collection = db.collection('articles');
|
|
539
|
+
|
|
540
|
+
const result = await collection.insertOne({ /* ... */ });
|
|
541
|
+
return result;
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error('Database operation failed:', error);
|
|
544
|
+
throw new Error('Failed to save article');
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### 2. 连接池管理
|
|
550
|
+
|
|
551
|
+
MongoDB 驱动会自动管理连接池,无需手动处理。
|
|
552
|
+
|
|
553
|
+
### 3. 避免 N+1 查询
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// ❌ 糟糕的做法 - N+1 查询
|
|
557
|
+
async getArticlesWithAuthors() {
|
|
558
|
+
const articles = await collection.find().toArray();
|
|
559
|
+
|
|
560
|
+
for (const article of articles) {
|
|
561
|
+
article.author = await usersCollection.findOne({
|
|
562
|
+
_id: article.authorId
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return articles;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// ✅ 好的做法 - 使用聚合
|
|
570
|
+
async getArticlesWithAuthors() {
|
|
571
|
+
return await collection.aggregate([
|
|
572
|
+
{
|
|
573
|
+
$lookup: {
|
|
574
|
+
from: 'users',
|
|
575
|
+
localField: 'authorId',
|
|
576
|
+
foreignField: '_id',
|
|
577
|
+
as: 'author'
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
{ $unwind: '$author' }
|
|
581
|
+
]).toArray();
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### 4. 分页优化
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
async getArticlesPaginated(page: number, limit: number) {
|
|
589
|
+
const db = this.mongodbService.getDb();
|
|
590
|
+
const collection = db.collection('articles');
|
|
591
|
+
|
|
592
|
+
// 使用游标分页(更高效)
|
|
593
|
+
const skip = (page - 1) * limit;
|
|
594
|
+
|
|
595
|
+
return await collection.find()
|
|
596
|
+
.skip(skip)
|
|
597
|
+
.limit(limit)
|
|
598
|
+
.sort({ createdAt: -1 })
|
|
599
|
+
.toArray();
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### 5. 数据缓存
|
|
604
|
+
|
|
605
|
+
```typescript
|
|
606
|
+
import NodeCache from 'node-cache';
|
|
607
|
+
|
|
608
|
+
const cache = new NodeCache({ stdTTL: 300 }); // 5分钟缓存
|
|
609
|
+
|
|
610
|
+
async getCachedArticle(id: string) {
|
|
611
|
+
// 检查缓存
|
|
612
|
+
const cached = cache.get(id);
|
|
613
|
+
if (cached) {
|
|
614
|
+
return cached;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// 从数据库获取
|
|
618
|
+
const article = await this.getArticleById(id);
|
|
619
|
+
|
|
620
|
+
// 存入缓存
|
|
621
|
+
cache.set(id, article);
|
|
622
|
+
|
|
623
|
+
return article;
|
|
624
|
+
}
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## 完整示例
|
|
628
|
+
|
|
629
|
+
查看博客插件中的数据持久化实现:
|
|
630
|
+
|
|
631
|
+
- 📁 [Blog Plugin 示例](../../examples/blog-plugin/src/server/services/ArticleService.ts)
|
|
632
|
+
|
|
633
|
+
## 相关文档
|
|
634
|
+
|
|
635
|
+
- 🔒 [权限管理](./permissions.md)
|
|
636
|
+
- 🎨 [UI 扩展插槽](./ui-slots.md)
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
**上一篇**: [权限管理](./permissions.md) | **下一篇**: [最佳实践](../best-practices/naming-conventions.md)
|