Files
skill-template/references/DATA_SOURCES.md
2026-11-01 16:30:46 +08:00

227 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 多数据源声明(SQLite / MySQL / 飞书多维表)
本文说明技能如何向匠厂宿主**声明**业务数据库,以及技能 CLI / Action 如何与宿主读写**同一物理库**(或同一飞书表)。
**产品定位:** 默认仍是本地 SQLite(`{skill_slug}.db`)。需要多用户共享、集中运维或云库时,可显式声明 **MySQL**。需要以飞书多维表格为权威数据时,声明 **`feishu_bitable`**。UI / API 契约尽量一致;SQLite/MySQL 的 `_jiangchang_*` 展示元数据写在同一逻辑库中,飞书源用本文件的 **`columns` 覆盖**声明展示。
**宿主版本:**
| 能力 | 最低 Host |
|------|-----------|
| `data-sources.json` + MySQL | **≥ 6.9.28** |
| `kind: feishu_bitable` + `columns` 展示覆盖 | 以当前匠厂 Host 为准(task-plaza 等已落地;发布说明见 Host changelog) |
**完整参考实现:**
- MySQL:[`process-work-order`](https://git.jc2009.com/client-commons/process-work-order) @ v1.0.2
- 飞书:业务技能 `task-plaza`(外包任务板)
---
## 何时用哪种后端
| 场景 | 推荐 |
|------|------|
| 单机、离线、小数据量、便携部署 | **SQLite**(默认,不配声明文件亦可) |
| 多用户共享、运维集中管库、与外部系统共库 | **MySQL 云库**(显式 opt-in) |
| 权威数据已在飞书多维表、多人在线协作 | **`feishu_bitable`**(显式 opt-in) |
**硬规则:一个 `sourceId` 对应一个物理库 / 一张飞书业务表声明。** 禁止双写、禁止 SQLite 与 MySQL / 飞书同步镜像。
---
## 文件位置
均在**用户数据目录**(不是安装包目录):
```
{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill_slug}/
├── data-sources.json # 声明式数据源(可选;见下)
├── .env # MYSQL_DSN / 飞书凭证等连接配置
└── {skill_slug}.db # SQLite 模式下的本地库文件
```
安装/升级时,若用户目录尚无 `data-sources.json` 且技能包内有 `assets/data-sources.example.json`,宿主会**复制一次**示例(不覆盖已有文件)。
模板默认示例为 **SQLite 显式声明**(与自动扫描 `{skill_slug}.db` 等价)。改走 MySQL / 飞书时,请参照 `assets/examples/` 下对应示例手工替换用户目录中的 `data-sources.json`,并在 `.env` 配置密钥。
若不需要声明式接入,可删除用户目录中的 `data-sources.json`,宿主恢复自动扫描 `.db` 文件。
---
## `data-sources.json` 格式
根对象:
| 字段 | 类型 | 说明 |
|------|------|------|
| `version` | `1` | 固定为 1 |
| `sources` | 数组 | 至少一条数据源 |
每条 `sources[]`:
| 字段 | 必填 | 说明 |
|------|------|------|
| `id` | 是 | 英文标识,如 `primary` |
| `kind` | 是 | `sqlite` \| `mysql` \| `feishu_bitable` |
| `displayName` | 否 | 侧栏展示名;多库时建议填写 |
| `path` | sqlite 必填 | 相对技能数据目录的路径;可用 `{skillSlug}` 占位 |
| `connectionEnv` | mysql 必填 | `.env` 中 DSN 的环境变量名,如 `MYSQL_DSN` |
| `appIdEnv` / `appSecretEnv` / `appTokenEnv` | feishu 必填 | `.env` 中飞书应用与多维表 app_token 的变量名 |
| `tableIdEnv` | feishu 常用 | `.env` 中业务表 table_id 的变量名(或宿主约定的等价字段) |
| `columns` | 否 | **列展示覆盖**(源无关;见下节) |
### SQLite 示例(模板默认)
见 [`../assets/data-sources.example.json`](../assets/data-sources.example.json)。
### MySQL 示例
见 [`../assets/examples/data-sources.mysql.example.json`](../assets/examples/data-sources.mysql.example.json)。
### 飞书多维表示例
见 [`../assets/examples/data-sources.feishu_bitable.example.json`](../assets/examples/data-sources.feishu_bitable.example.json)。
---
## `columns` 展示覆盖(源无关)
连接器先按后端生成列 schema(SQLite `_jiangchang_columns` / MySQL 元数据 / 飞书字段映射),再叠加 `sources[].columns`:
```json
"columns": {
"状态": { "editable": false, "searchable": true },
"结果链接": { "displayType": "url", "editable": false, "searchable": false },
"发单人": {
"displayType": "userId",
"editable": false,
"formVisible": false,
"defaultValueFrom": "currentUser.userId",
"searchable": true
},
"平台": {
"searchable": true,
"optionsWhen": {
"dependsOn": "任务类型",
"map": { "文章发布": ["头条号", "搜狐号"], "视频发布": ["抖音", "视频号"] }
}
}
}
```
| 字段 | 类型 | 作用 |
|------|------|------|
| `displayType` | string | 覆盖展示类型(如 `url`、`textarea`、`userId`、`currency`) |
| `displayName` | string | 覆盖列显示名 |
| `defaultValue` | string/number/boolean/null | 新增表单默认值 |
| `defaultValueFrom` | string | 从宿主会话预填:`currentUser.userId` / `currentUser.username` / `currentUser.usernameOrUserId` |
| `optionsWhen` | object | 依赖另一字段当前值过滤下拉:`dependsOn` + `map` |
| `createable` | boolean | `false` = 不进新增表单 / 不进 create payload |
| `editable` | boolean | `false` = 不进编辑表单 / 不进 update payload(状态机字段常用) |
| `formVisible` | boolean | `false` = 表单不展示但仍可按 createable/editable 写入(如静默 `defaultValueFrom`) |
| `requiredOnCreate` | boolean | 新增必填 |
| `searchable` | boolean | 是否进**列筛选**与**顶栏全局搜索**(覆盖连接器默认) |
**与 `_jiangchang_columns` 的关系:**
| 后端 | 主声明位置 |
|------|------------|
| SQLite / MySQL | `_jiangchang_columns`(`searchable` / `editable` / `display_type` 等);`data-sources.columns` 可再覆盖 |
| 飞书 | **无** `_jiangchang_*` 表;**以 `data-sources.json` 的 `columns` 为准**覆盖飞书字段映射默认值 |
飞书连接器默认:明文与单选等常见字段可筛;技能用 `searchable: true/false` 收紧或放开筛选列(避免「全表可筛」噪音)。详见 [`SCHEMA.md`](SCHEMA.md)「列筛选与全局搜索」与「`display_type: url` 外开」。
---
## `.env` 与连接串
密钥写在用户 `.env`(或进程环境变量),**不要**写进 `data-sources.json`:
```env
# MySQL 云库连接(不配则走本地 SQLite)
MYSQL_DSN=mysql://user:password@192.168.0.20:3306/your_database
# 飞书多维表(变量名须与 data-sources.json 中 *Env 一致)
FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=xxx
FEISHU_BITABLE_APP_TOKEN=bascnxxx
FEISHU_TASK_TABLE_ID=tblxxx
```
MySQL 格式:`mysql://` 或 `mysql2://`,须含库名。注释写法须符合 [`../development/CONFIG.md`](../development/CONFIG.md)。
技能 Python 侧(SQLite/MySQL):`scripts/db/backend.py` 读取同一 `MYSQL_DSN`;有值则 `resolve_backend()` 为 `mysql`,否则 `sqlite`。飞书读写由技能自备 SDK / HTTP,与宿主连接器并行遵守同一张表、同一凭证 env。
---
## 技能代码职责
### 宿主 UI(数据管理)
宿主读取用户目录 `data-sources.json` + `.env`,经 Host 内置连接器访问库 / 飞书表。**不依赖**技能 `connection.py`。
### CLI / Action / `init-db`
**必须**与宿主使用同一后端策略,否则会出现「UI 看云库、CLI 写本地 `.db`」的分裂。
模板已提供(SQLite / MySQL):
| 模块 | 作用 |
|------|------|
| `scripts/db/backend.py` | `resolve_backend()`、`parse_mysql_dsn()` |
| `scripts/db/cursor_compat.py` | 统一 `?` 占位符与 `executescript` |
| `scripts/db/connection.py` | `get_conn()` 双分支;`init_db()` 幂等建 `_jiangchang_*` |
业务表 DDL / 迁移仍在技能自己的 `schema` / `init_db()` 中按 `conn.backend` 分支处理(见工单参考实现)。飞书技能无本地业务 DDL,但仍须提供 `health` / Action 与宿主同一表。
可选:`describe_active_store()` 用于 health / 排障。
---
## MySQL / 飞书 当前限制(Host)
**MySQL:**
- 不支持 `tableAdmin`(建表/改表 UI)
- 不支持 `cellPresentation`
- 元数据与 CRUD 与 SQLite 路径一致
**飞书:**
- 不支持本地式 `tableAdmin` / 随意改物理 DDL(字段以飞书后台为准)
- 筛选/排序能力受飞书 API 约束;用 `columns.searchable` 控制筛选项
- 附件 / 人员等字段依赖宿主映射的 `displayType`(如 `attachment`、`userId`)
表结构变更:SQLite/MySQL 由技能 `init-db` / 迁移负责;飞书在飞书后台改字段后,同步更新 `columns` 覆盖与技能读写字段名。
---
## 开发 Checklist(云库 / 飞书)
复制新业务技能并选择 MySQL 时:
1. Host ≥ 6.9.28
2. 用户目录放置 `data-sources.json`(mysql 源 + `connectionEnv`)
3. `.env` 配置 `MYSQL_DSN`(或你在 json 里声明的 env 名)
4. `requirements.txt` 含 `PyMySQL>=1.1.0`(模板已声明)
5. 业务 DDL 同时支持 sqlite / mysql 分支(或仅部署 mysql)
6. **抽测**:数据管理 UI 与 CLI(如 list / create)读写同一库
7. 见 [`../development/CHECKLIST.md`](../development/CHECKLIST.md) A6-16
选择飞书时:
1. Host 已支持 `feishu_bitable`
2. `data-sources.json` + `.env` 凭证齐全
3. `columns` 收紧 `searchable` / `editable`,结果链接用 `displayType: url`
4. CLI / Action 与数据管理抽测同一 `app_token` + `table_id`
5. 见 CHECKLIST **A6-17**
---
## 与 SCHEMA / ACTIONS 的关系
- [`SCHEMA.md`](SCHEMA.md):`_jiangchang_*` 适用于 SQLite/MySQL 已注册表;飞书用本文件 `columns`。`url` 外开、列筛选语义两边对齐。
- [`ACTIONS.md`](ACTIONS.md):无 `replaces` 时宿主直写**已注册数据源**;状态机表常用 Policy 关裸编辑 + `replaces: delete` + `visibleWhen`。
- [`CLI.md`](CLI.md):`init-db` 对当前 active backend(sqlite/mysql)幂等初始化元数据。