概述
插件必须把数据与资源放在平台管理的目录中,以便在 Docker、预发与生产环境中路径可移植。AppFolder 会解析 process.env 中的路径。
- 运行时生成的文件 → APP_DATA_FOLDER 下的插件数据目录
- 打包的静态文件 → backend/assets(安装后只读)
- 临时 / 未完成上传 → 应用临时目录(可能被清理)
- 不要直接写入 APP_DATA_FOLDER 根目录——始终使用插件子目录
1. 应用与插件目录
四个位置很重要。全局目录来自环境变量;插件目录由插件名(资源还需版本)推导。
| 目录 | 路径 | 工具方法 | 用途 |
|---|---|---|---|
| App data (global) | process.env.APP_DATA_FOLDER | AppFolder.getAppDataFolder() | 所有持久应用数据的根——不要直接写入 |
| App temp | process.env.UPLOAD_FILE_TEMP_FOLDER | AppFolder.getAppTempDataFolder() | 临时文件(分段上传、草稿)——勿存关键数据 |
| Plugin data | <APP_DATA_FOLDER>/<pluginName> | AppFolder.getPluginDataFolder(pluginName) | 运行时数据:导出、报表、磁盘缓存 |
| Plugin assets | <INSTALLED_PLUGINS_FOLDER>/<pluginName>/<version>/backend/assets | AppFolder.getPluginAssetFolder(pluginName, version) | 随插件包分发的只读静态资源 |
2. 数据目录 vs 资源目录
应用运行期间创建或更新的文件用数据目录;随源码发布且运行时不修改的文件用资源目录。
- 数据示例:CSV、报表 PDF、本地 SQLite、运行时配置
- 资源示例:单据模板、seed JSON、默认图标
| 资源目录 | 数据目录 | |
|---|---|---|
| Purpose | 随代码打包的静态文件 | 运行时创建的动态文件 |
| Persistence | 重装 / 更新版本时会被替换 | 插件更新后仍保留 |
| Utility | getPluginAssetFolder(name, version) | getPluginDataFolder(name) |
| Source in repo | plugins/<name>/backend/assets/ | 运行时在磁盘上创建 |
3. AppFolder API
从 @quan-erp/shared-backend-core 导入 AppFolder。优先使用 metadata.name 与 metadata.pluginVersion。
| 方法 | 说明 |
|---|---|
| getAppDataFolder() | 应用数据根目录(APP_DATA_FOLDER) |
| getAppTempDataFolder() | 临时上传 / 草稿目录 |
| getPluginDataFolder(pluginName) | 单个插件的持久数据目录 |
| getPluginAssetFolder(pluginName, version) | 已安装插件某版本的 backend/assets |
TSXresolve paths
1import { AppFolder } from "@quan-erp/shared-backend-core";
2import metadata from "../../module.metadata.json" with { type: "json" };
3import fs from "node:fs/promises";
4import path from "node:path";
5
6const dataDir = AppFolder.getPluginDataFolder(metadata.name);
7const assetDir = AppFolder.getPluginAssetFolder(
8 metadata.name,
9 metadata.pluginVersion,
10);
11
12// Persistent runtime file
13await fs.mkdir(dataDir, { recursive: true });
14await fs.writeFile(path.join(dataDir, "export.csv"), csv, "utf8");
15
16// Bundled template (read-only)
17const template = await fs.readFile(
18 path.join(assetDir, "invoice-template.html"),
19 "utf8",
20);4. 打包后端资源
将静态后端文件放在 plugins/<name>/backend/assets/。构建 / 打包会把该目录打进插件包,安装后可用 getPluginAssetFolder 解析。
- backend/assets/ 下的全部内容都会被打包
- 安装后使用 getPluginAssetFolder(name, pluginVersion)
- 运行时不要写入资源目录——写入 getPluginDataFolder
SHsource layout
1plugins/<plugin-name>/
2└── backend/
3 ├── assets/
4 │ ├── invoice-template.html
5 │ └── seed/
6 │ └── defaults.json
7 └── src/规则
- 隔离 — 只写入 getPluginDataFolder(yourPluginName)
- 持久化 — 不要把关键数据放在 getAppTempDataFolder()
- 可移植 — 始终用 AppFolder;不要硬编码路径
- 版本化资源 — 向 getPluginAssetFolder 传入 pluginVersion