chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:31:17 +08:00
commit 4ca82fb58a
203 changed files with 45745 additions and 0 deletions

27
docs/README.md Normal file
View File

@@ -0,0 +1,27 @@
# 文档目录
本目录为智能体「生成 / 发布 / 灌数」相关说明(中文)。
| 文档 | 说明 |
|------|------|
| [智能体-生成发布-能力说明.md](./智能体-生成发布-能力说明.md) | **需求 / 能力边界**(做什么、不做什么、工作流、验收) |
| [智能体-生成发布-API.md](./智能体-生成发布-API.md) | **接口契约**(路径、请求/响应、宿主回执字段) |
| [数据同步-中间件.md](./数据同步-中间件.md) | 跨库实时同步SQLite/MySQL/Postgres |
> 业务用语称「**模块**」。HTTP 路径仍为 `/api/v1/apps/...`。智能体默认无需配置模块白名单即可自建发布。
## Linux 部署
本仓库根目录提供 `start.sh` / `restart.sh` / `stop.sh` / `pull-and-restart.sh`
与宇恒 Web 合并的交互菜单见同级目录 [`../ops/README.md`](../../ops/README.md)`./deploy-menu.sh`)。
**配置热重载**(改域名/Nginx/yaml 不必整栈 rebuild[`../nginx/README.md`](../nginx/README.md),执行 `./reload-config.sh`
| 改什么 | 文件 | 命令 |
|--------|------|------|
| 容器反代 | `web/nginx.conf` | `./reload-config.sh web` |
| 公开 URL / 发布回执 | `.env``AIJZ_PUBLIC_BASE_URL` + `platform/etc/platform.docker.yaml` | `./reload-config.sh platform` |
| Gateway | `gateway/etc/gateway.docker.yaml` | `./reload-config.sh gateway` |
| LLM Key | `.env` | `./reload-config.sh ai` |
| 供应商 / 模型列表 | `ai-service/etc/llm.yaml` | `./reload-config.sh ai` |
| 域名 HTTPS | `nginx/aijz.host.conf` + 证书 | `./reload-config.sh host-nginx` |

View File

@@ -0,0 +1,53 @@
# 登录策略与外公司部署授权
## 删文件后旧包还能用吗?
分几层:
| 客户删了什么 | 能否拦住旧延期包 |
|--------------|------------------|
| 只删 `leases/*.json` | **能** — 消费记录在 `state/_consumed.json` |
| 只删 leases + 改/清 state 文件 | **能**(有库时)— Postgres `license_consumed` 双写,可恢复 |
| **leases + state + 数据库全删** | 本地拦不住 → 需配置 **`RedeemURL` 联网核销**,或你们**永不重签同一 id** |
纯离线、客户把机器数据全部清空,没有任何方案能 100% 防复用(等于新装机)。要硬保证:配核销服务,或只发一次性 id 且服务端登记。
---
## 推荐:延期软件 + 分层持久化
```text
./data/license/leases/ # 租约文件(可删)
./data/license/state/ # _consumed.json 已消费 id签名
Postgres license_consumed # 第二副本
RedeemURL可选 # 你们服务端核销 id
```
### yaml
```yaml
License:
Enabled: true
Customer: "A公司"
LeaseDir: "./data/license/leases"
StateDir: "./data/license/state"
ControlSecret: "换成强密钥"
RedeemURL: "https://license.你们的域名" # 建议生产打开
SeedNotAfter: "2027-07-30"
```
`RedeemURL` 时:导入包会 `POST {RedeemURL}/v1/license/redeem`,服务端若该 id 已核销则拒绝。删光客户机数据后旧包仍无效。
临时延期:**仅 1 次**、最多 30 天。同一本地 id 不可二次导入。
---
## API
`X-License-Secret``renew` / `extend` / `import`
```bash
curl -X POST http://客户机:8888/api/v1/license/import \
-H "X-License-Secret: 强密钥" -H "Content-Type: application/json" \
--data-binary @pack.json
```

View File

@@ -0,0 +1,100 @@
# 跨库数据同步中间件
支持 **SQLite ↔ MySQL ↔ Postgres**,不要求两端同一种数据库。变更经 **outbox 队列** 近实时投递;冲突进 **冲突队列**
## 权限与隔离
| 项 | 说明 |
|----|------|
| 谁可配 | 仅公司**顶级权限(管理员)**,权限名「数据同步」 |
| 谁不可 | 编辑 / 只读、智能体账号(即使有「发布模块」) |
| 数据隔离 | 通道与冲突带 `tenant_id`;公司 A 看不到公司 B 的通道/DSN |
| 多服务器 | 同一公司可建多条通道,分别填 B、C 等库的 DSN |
## 典型场景A / B / C
| 端 | 角色 |
|----|------|
| **A** | 线上库(用户增删改) |
| **B** | 本地库(本机业务 + 接收 C |
| **C** | 额外数据源Excel / API / 导入),只写入 **B** |
推荐配置:
1. 建一条通道:`local` = B`remote` = A**方向 `bidirectional`**,冲突策略 `queue`(或 LWW
2. C 的数据用 **ingest API**(或业务直接写 B写入本地触发器进 outbox再推到 A。
3. A 上用户改的数据经 outbox 拉回 B。
4. 怀疑漏数时点 **对账**,或等双向通道约每分钟自动对账。
如何保证**不漏、不多**
| 手段 | 防什么 |
|------|--------|
| 表触发器 → `_ajz_sync_outbox` | 漏(本地/线上变更必入队) |
| 应用远端时 `WithApplying`(触发器不写 outbox | 多A↔B 回声环) |
| 目标 meta 版本相等则跳过 | 多(重复投递) |
| 目标版本更新 → 冲突队列 / LWW | 并发改同一行 |
| 主键对账 reconcile | 漏(存量差、触发器未装前的行) |
| C→B upsert 同主键 | 多(重复灌入) |
```
C ──ingest/写库──► B (local) ◄──bidirectional outbox──► A (remote)
```
## 能力
| 项 | 说明 |
|----|------|
| 方言 | `sqlite` / `mysql` / `postgres` |
| 实时性 | 表触发器写 `_ajz_sync_outbox`worker 默认每 500ms 拉取 |
| 方向 | 本地→线上 / 线上→本地 / **双向**A↔B 场景用这个) |
| 冲突 | `queue`(入队)/ `lww_source` / `lww_target` |
| 对账 | `POST .../reconcile`;双向运行中约每分钟自动一次 |
| 外部源 | `POST .../ingest`C → B再同步到 A |
| 配置 | 控制台「数据同步」页;可改线上 DSN |
配置与冲突持久化:`data/dbsync/channels.json``conflicts.json`Docker`.runtime/dbsync`)。
## 控制台用法
1. 登录 → **数据同步****新建通道**
2. 本地 B`sqlite` + `file:./data/local.db`,表名逗号分隔
3. 线上 A`mysql` + `user:pass@tcp(host:3306)/db?parseTime=true`
4. 方向选 **双向****测试连接****保存****启动**
5. 需要补漏时点 **对账**C 数据走业务写 B 或调用 ingest API
## API需公司顶级权限「数据同步」/ 管理员)
| 方法 | 路径 |
|------|------|
| GET/POST | `/api/v1/admin/sync/channels` |
| GET/PUT/DELETE | `/api/v1/admin/sync/channels/{id}` |
| POST | `/api/v1/admin/sync/test` |
| POST | `/api/v1/admin/sync/channels/{id}/prepare\|start\|stop` |
| POST | `/api/v1/admin/sync/channels/{id}/reconcile` |
| POST | `/api/v1/admin/sync/channels/{id}/ingest` |
| GET | `/api/v1/admin/sync/conflicts` |
| POST | `/api/v1/admin/sync/conflicts/{id}/resolve` |
### ingest 示例
```json
POST /api/v1/admin/sync/channels/{id}/ingest
{
"table": "article",
"source": "excel",
"rows": [
{ "id": "c-001", "title": "来自 C" }
]
}
```
按主键 upsert 写入本地 B触发器入 outboxworker 再推到线上 A。
## 注意
- 两端业务表结构需兼容(同名列);主键默认 `id`,可用 `pk_columns` 覆盖。
- MySQL 需账号有建触发器权限。
- 密钥在 DSN 中;列表页会打码显示。
- 「实时」为亚秒级轮询 + 触发器,非 MySQL binlog CDC同机延迟通常 &lt;1s。
- 对账按**主键集合**补缺行,不做逐字段内容 diff同 PK 内容冲突仍靠版本 / 冲突队列。

View File

@@ -0,0 +1,450 @@
# 智能体 · 生成与发布 API
Base URL`http://127.0.0.1:8180`(网关)
需求 / 能力说明见:[智能体-生成发布-能力说明.md](./智能体-生成发布-能力说明.md)
> 业务用语:**模块**。API 路径仍为 `/api/v1/apps/...`。
> **智能体默认无需配置 `app_slugs`**:启用「生成发布」后可自由发布自建模块;`app_slugs` 仅在需要白名单限制时填写。
> **禁止**在已发布模块的业务库查用户 / 权限。账号绑定只走「首次登记」。
---
## 总览
| 步骤 | 方法 | 路径 | 鉴权 |
|------|------|------|------|
| 首次登记 | POST | `/api/v1/auth/agent/register` | 公开 + `register_secret` |
| 换票 | POST | `/api/v1/auth/token` | 公开client_credentials |
| 列模块 | GET | `/api/v1/apps` | Bearer + 「读取模块」(管理=全部;智能体默认不限制) |
| 登记在建 | PUT | `/api/v1/apps/{slug}/draft` | Bearer + 「发布模块」 |
| 读蓝图 | GET | `/api/v1/apps/{slug}/blueprint` | Bearer + 「读取模块」 |
| 列模型 | GET | `/ai/api/v1/llm/providers` | 公开 |
| 生成 | POST | `/api/v1/apps/generate` | 网关可公开 |
| 发布 | POST | `/api/v1/apps/{slug}/publish` | Bearer + 「发布模块」 |
| 公开读(加密路径) | GET | `/api/v1/public/m/{token}/blueprint` | 公开 |
| 导入 | POST | `/api/v1/apps/{slug}/{resource}/import` | Bearer + `row.import` |
| 抽查 | GET | `/api/v1/apps/{slug}/{resource}` | Bearer + `row.read` |
决策树:
```text
GET /apps本账号可见模块
├─ 选中已有模块 slug
│ → GET blueprint避开已有 page id/route
│ → generate生成新页面
│ → publish mode=add_pages|auto
│ → import
└─ 无合适模块
→ generate完整多页蓝图
→ publish mode=create|auto自定新 slug无需预授权
→ import
```
---
## 0. 首次登记
`POST /api/v1/auth/agent/register`(公开)
宿主第一次接入时调用。必须上传:
| 宿主侧 | API 字段 | 必填 | 说明 |
|--------|----------|------|------|
| 宇恒 ID | `host_key` | 是 | 稳定唯一标识,幂等绑定 |
| 名称 | `name` | 是 | 控制台显示名 |
| — | `tenant_id` | 否 | 默认 `1` |
| — | `register_secret` | 是 | 与平台注册密钥一致 |
```json
{
"name": "宇恒节点显示名",
"host_key": "<宇恒ID>",
"tenant_id": 1,
"register_secret": "dev-only-change-me"
}
```
响应含 `client_id` / `client_secret`(只返回一次),`status``pending`
管理员在控制台选角色「生成发布」并启用后即可换票。**可访问模块可留空**(留空=可自由发布自建模块)。
同一 `host_key`pending 重连会轮换 secret已 active 再 register → 409。
---
## 1. 换票
`POST /api/v1/auth/token`(公开)
```json
{
"grant_type": "client_credentials",
"client_id": "agt_xxx",
"client_secret": "明文密钥"
}
```
`access_token`。后续列模块 / 读蓝图 / 发布 / 导入须:
`Authorization: Bearer <access_token>`
### 权限与模块授权
| 权限 | 用途 |
|------|------|
| 「发布模块」 | 发布模块(必选) |
| 「读取模块」 | 列模块、读蓝图(必选) |
| `row.import` | 灌数(必选) |
| `row.read` | 抽查(建议) |
| `storage.write` / `storage.read` | 素材(建议) |
推荐角色:**生成发布(`publisher`**。
`app_slugs`**可选白名单**。
- **留空(推荐默认)**:不限制,智能体可自定 slug 生成/发布
- 填写若干 slug 或 `*`:仅允许列表内(`*`=全部)
生成接口经网关可公开;发布与导入须鉴权(角色权限),**不强制**后台预授权模块。
---
## 2. 列出模块(先选目标)
`GET /api/v1/apps`
需 Bearer + 「读取模块」。
| 调用方 | 可见范围(`scope` |
|--------|---------------------|
| 管理账号(非 agent | `all`:本租户全部(含在建) |
| 智能体(`app_slugs` 空或含 `*` | `open`:不限制 |
| 智能体(配置了白名单) | `granted`:仅白名单 |
```bash
curl -s http://127.0.0.1:8180/api/v1/apps \
-H "Authorization: Bearer <access_token>"
```
```json
{
"scope": "all",
"items": [
{
"app_id": "...",
"slug": "settlement",
"name": "沉降观测",
"status": "draft",
"status_label": "在建",
"building": true,
"page_count": 4,
"entity_count": 2,
"updated_at": "2026-07-24T08:00:00Z",
"created_at": "2026-07-24T08:00:00Z"
}
]
}
```
| 情况 | 下一步 |
|------|--------|
| 有目标模块(含在建) | 记下 `slug` → 读蓝图 / 继续生成 → `publish` |
| 没有 | 选定新 `slug` → generate → `PUT .../draft` 登记在建 → `publish` 上线 |
### 2.1 登记在建模块
生成蓝图后、正式发布前:
`PUT /api/v1/apps/{slug}/draft`
Body`{ "blueprint": { ... } }`
需 「发布模块」。不跑 DDL状态为 `draft`(在建)。已发布模块不可用本接口覆盖。
---
## 3. 读取已发布/在建蓝图
`GET /api/v1/apps/{slug}/blueprint`
需 Bearer + 「读取模块」 + 该模块已授权。
用途:
- 生成新页面前,收集已有 `pages[].id` / `route`,避免冲突
- 发布后自检
---
## 4. 列出大模型(可选)
`GET /ai/api/v1/llm/providers`(公开)
兼容:`http://127.0.0.1:8180/ai/api/v1/llm/providers`
```json
{
"default": "deepseek",
"providers": [
{
"id": "deepseek",
"label": "DeepSeek",
"configured": true,
"default_model": "deepseek-chat",
"models": [{ "id": "deepseek-chat", "label": "deepseek-chat" }]
}
]
}
```
---
## 5. 生成蓝图 / 生成新页面
`POST /api/v1/apps/generate`
(网关转发 AI兼容 `POST /ai/api/v1/apps/generate`
`Content-Type: multipart/form-data`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `prompt` | string | 建议 | 需求描述。**已有模块**时写清:目标 slug、要新增哪些页面、勿复用已有 id/route |
| `storage_mode` | string | 否 | `schema_per_app`(默认)或 `database_per_app` |
| `llm_provider` | string | 否 | 默认 `deepseek` |
| `llm_model` | string | 否 | 空则用厂商默认 |
| `excel` | file | 否 | xlsx / csv / json |
| `data_file` | file | 否 | 同 excel |
| `images` | file[] | 否 | 界面截图 |
| `layout_files` | file[] | 否 | 页面 HTML/MHTML |
```bash
# 示例:为已有模块生成新页面
curl -s http://127.0.0.1:8180/api/v1/apps/generate \
-F "prompt=目标模块 settlement。已有 page id: dash_main, list_records。请新生成「测点筛选」列表+新建表单id/route 勿冲突。" \
-F "storage_mode=schema_per_app" \
-F "llm_provider=deepseek" \
-F "llm_model=deepseek-chat" \
-F "excel=@./settlement.xlsx" \
-F "images=@./board_a.png"
```
成功响应要点:
```json
{
"draft": {
"version": "1.0",
"meta": { "slug": "settlement", "name": "..." },
"entities": [],
"apis": {},
"pages": [],
"storage": {},
"security": {}
},
"warnings": [],
"confidence": 0.86,
"require_confirm": true,
"llm_provider": "deepseek",
"llm_model": "deepseek-chat",
"fidelity": { "skipped": false, "target": 95, "final_score": 96, "passed": true },
"generate_log": { "run_id": "...", "lines": [] }
}
```
| 字段 | 说明 |
|------|------|
| `draft` | 蓝图;发布时提交(可微调) |
| `draft.meta.slug` | 新建模块时用此 slug已有模块发布时以路径 `{slug}` 为准 |
| `draft.pages` | **禁止只生成 1 个 page**;数量不设上限 |
| `draft == null` | 生成失败,看 `warnings` / `generate_log` |
### 页面约定
| type | 用途 |
|------|------|
| `list` | 列表(建议 `actions``import` |
| `form_create` | 新建表单 |
| `form_edit` | 编辑表单 |
| `dashboard` | 看板 |
| `detail` | 详情 |
每个 page 必须有独立 `id` / `title` / `route` / `entity`
```json
"layout": {
"columns": ["sku", "qty", "status"],
"actions": ["create", "edit", "delete", "export", "import", "refresh"]
}
```
**已有模块**`draft` 应主要是**新生成的 pages**(及对应 entities / apis不要把旧页面再写一遍。
**新建模块**`draft` 为完整多页蓝图。
---
## 6. 发布(宿主 → 建站平台)
宿主(宇恒)侧「发布」应把建站蓝图与展示元数据 **POST 到本建站平台**;平台落库建站,并**按用户/智能体 ID 加密出文件路径**,回执给宿主表格展示。
`POST /api/v1/apps/{slug}/publish`
Headers
- `Authorization: Bearer <access_token>`
- `Content-Type: application/json`
Path `{slug}` = 选定的目标**模块**(新建时为新 slug`blueprint.meta.slug` 会对齐到路径 slug。
默认无需预授权;仅当账号配置了 `app_slugs` 白名单时才校验。
```json
{
"mode": "auto",
"blueprint": { },
"host_meta": {
"module_name": "whm123",
"publish_style": "immediate",
"host_base_url": "https://whm123.yuheng.com"
}
}
```
| 字段 | 说明 |
|------|------|
| `mode` | 见下表 |
| `blueprint` | 建站蓝图(必填) |
| `host_meta.module_name` | 宿主表格「模块名称」 |
| `host_meta.publish_style` | `immediate` → 立即发布上线 |
| `host_meta.host_base_url` | 宿主访问域名;有则作为回执 `access_url` |
| `mode` | 含义 |
|--------|------|
| `auto`(默认) | 模块已存在 → 发布新生成的页面;不存在 → 新建模块 |
| `add_pages` | 必须已存在;发布草稿中的**新页面** |
| `create` | 必须不存在;新建模块 |
| `replace` | 整份覆盖(慎用) |
日常用 `auto` 或显式 `add_pages` / `create`
### 加密访问路径
平台用当前账号的 **用户/智能体 IDowner_id** + 租户 + 模块 slug加密生成逻辑文件路径
- `access_path`:如 `m/ajzm1_...`(路径中**无明文 slug / 用户 id**
- `access_url`:优先宿主 `host_base_url`;否则 `{PublicBaseURL}/api/v1/public/{access_path}/blueprint`
公开读取蓝图(无需登录):
`GET /api/v1/public/m/{token}/blueprint`
其中 `{token}``access_path` 去掉前缀 `m/` 的段。
### 宿主「AI 表格数据」字段映射
| 宿主表格 | 取自发布响应 |
|----------|----------------|
| 模块名称 | `module_name` |
| 发布方式 | `publish_style``immediate` → 立即发布上线) |
| 访问地址 | `access_url`(或宿主自拼域名 + `access_path` |
| 发布时间 | `published_at` |
| 状态 | `status` = `published` → 已发布 |
下一步仍是:**灌数 → 打开模块**。
### 已有模块(`auto` / `add_pages`
- 保留该模块已有 pages / entities / apis
- 发布草稿中**新生成**的 page`id``route` 不可冲突)
- 新 entity / resource 一并加入;已有 entity 只追加新字段
- 若没有任何新 page / entity / resource → 400
### 新建模块时 blueprint 至少含
- `meta.slug` / `meta.name`
- `version`(如 `"1.0"`
- `storage``{ "mode": "schema_per_app", "engine": "postgres" }`
- `security``{ "visibility": "private", "roles": [], "row_policies": [] }`
- `apis.base_path` = `/api/v1/apps/{slug}`
- `entities` / `apis` / `pages`
```bash
curl -s http://127.0.0.1:8180/api/v1/apps/settlement/publish \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"mode":"create","host_meta":{"module_name":"whm123","publish_style":"immediate","host_base_url":"https://whm123.yuheng.com"},"blueprint":{...}}'
```
响应示例:
```json
{
"app_id": "...",
"slug": "settlement",
"schema_name": "app_t1_settlement",
"status": "published",
"publish_mode": "created",
"module_name": "whm123",
"publish_style": "immediate",
"access_path": "m/ajzm1_...",
"access_url": "https://whm123.yuheng.com",
"published_at": "2026-07-24T02:00:00Z",
"owner_id": 3,
"endpoints": ["GET /api/v1/apps/settlement/blueprint", "..."],
"ddl": [],
"memory_mode": false
}
```
| `publish_mode` | 含义 |
|----------------|------|
| `created` | 新建了模块 |
| `pages_added` | 向已有模块发布了新页面 |
| `replaced` | 整份覆盖 |
| HTTP | 原因 |
|------|------|
| 401 | token 无效 |
| 403 | 缺 「发布模块」;或配置了白名单但不含该 slug |
| 400 | 蓝图非法 / page id 冲突 / mode 与是否存在不一致 |
---
## 7. 导入业务数据(发布后必做)
发布只建表,**不自动灌数**。
`POST /api/v1/apps/{slug}/{resource}/import`
`Content-Type: multipart/form-data`
需 Bearer + `row.import` + 模块授权。
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | xlsx / csv / json |
`resource` = `apis.resources[].path`(无前导 `/`),如 `records`
```bash
curl -s "http://127.0.0.1:8180/api/v1/apps/settlement/records/import" \
-H "Authorization: Bearer <access_token>" \
-F "file=@./settlement.xlsx"
```
```json
{ "inserted": 120, "skipped": 0, "errors": [] }
```
抽查:
`GET /api/v1/apps/{slug}/{resource}?page=1&page_size=5`(需 `row.read`
确认 `total > 0`,否则任务未完成。
---
## 8. 推荐调用顺序
```text
0. POST /api/v1/auth/agent/register 宇恒ID(host_key) + 名称(name)
1. (管理员)启用 + 角色 publisher模块白名单可选默认留空
2. POST /api/v1/auth/token
3. GET /api/v1/apps 先选模块(仅本账号可见)
4. GET /api/v1/apps/{slug}/blueprint 仅已有模块:避让已有 page
5. POST /api/v1/apps/generate 生成新页面 或 完整蓝图
6. POST /api/v1/apps/{slug}/publish mode=auto | add_pages | create
7. POST /api/v1/apps/{slug}/{resource}/import
8. GET /api/v1/apps/{slug}/{resource}?page=1&page_size=5
```
不要:查业务库用户表;行级增删改(除 importadmin/audit对已有模块默认 `replace`
语义:已有模块上是 **生成新页面再发布**;新建模块自定 slug 即可,无需后台预授权。

View File

@@ -0,0 +1,185 @@
# 智能体能力说明:生成、发布与灌数
一类专用智能体:先选定**模块**,再生成页面并发布到建站平台,最后导入业务数据。
接口细节见:[智能体-生成发布-API.md](./智能体-生成发布-API.md)
> 业务用语称「**模块**」API 路径仍为 `/apps`,字段仍为 `app_slugs`)。
> **管理账号**可查看本租户全部模块(含在建)。
> **智能体**:启用并赋予「生成发布」后即可**自由发布自建模块**`app_slugs` 留空表示不限制,无需后台逐个授权。
---
## 1. 一句话定位
| 项目 | 说明 |
|------|------|
| 做什么 | 登记账号 → 选模块 → **生成页面****向建站平台发布** → 导入数据 |
| 得到什么 | 发布回执含模块名、加密访问路径、状态;控制台「打开模块」见多页业务后台 |
| 管理侧 | 管理账号在「模块管理」看到全部模块(在建 / 已发布 / 失败) |
| 不做什么 | 查业务库用户表;角色 / 邀请 / 组织管理;行级增删改(仅允许 import + 只读抽查) |
---
## 2. 核心规则(必读)
### 2.1 绑定宇恒 ID首次必做
智能体账号**不在**已发布模块的业务表里。禁止在业务库翻用户 / 权限。
| 宿主侧 | API 字段 | 说明 |
|--------|----------|------|
| **宇恒 ID** | `host_key` | 稳定唯一标识;**必传** |
| **名称** | `name` | 控制台显示名;**必传** |
`POST /api/v1/auth/agent/register``pending` → 管理员赋「生成发布」并**启用** → 换票。
**不要求**再填写「可访问模块」才能发布。
### 2.2 管理账号与模块可见范围
| 账号类型 | `GET /api/v1/apps` 可见范围 |
|----------|------------------------------|
| **管理账号**(如 demo/`owner`,非 agent | 本租户**全部**模块,含 **在建** 与已发布、失败 |
| **智能体(`app_slugs` 为空或含 `*`** | 不限制(`scope=open`),可列本租户模块并自由发布自建 slug |
| **智能体(配置了白名单)** | 仅白名单内(`scope=granted`)——可选限制,非默认 |
状态展示:
| status | 中文 | 说明 |
|--------|------|------|
| `draft` / `validating` / `provisioning` | **在建** | 已生成蓝图或正在发布,尚未成功上线 |
| `published` | 已发布 | 可打开业务页、可灌数 |
| `failed` | 失败 | 发布失败,可继续编辑后重发 |
生成蓝图成功后可 `PUT /api/v1/apps/{slug}/draft` 登记为在建。
### 2.3 先选模块,再生成
发布前建议 `GET /api/v1/apps` 查看已有模块:
| 选择 | 含义 |
|------|------|
| **已有模块** | 为该模块 **生成新的页面**,再发布到该模块 |
| **没有合适模块** | **生成完整多页蓝图**,再 **新建模块**(自定 slug无需后台预授权 |
### 2.4 页面必须多页
一次生成应产出多个 `pages`(至少列表 + 新建表单;建议再加编辑 / 看板)。
页面数量**不设上限**。列表页建议带 `import`
### 2.5 发布 = 向建站平台送建站数据
宿主侧点击「发布」时,应把蓝图与展示元数据 **POST 到本建站平台**(不是只在宿主本地落库)。
平台会:
1. 校验并落库模块蓝图(新建或向已有模块发布新页面)
2. **按当前用户 / 智能体 ID 加密生成访问文件路径**`access_path`
3. 回执模块名称、发布方式、访问地址、发布时间、状态供宿主「AI 表格数据」展示
### 2.6 发布后必须灌数
发布只建结构,**不会自动灌 Excel**。成功后必须 `import`,并建议抽查 `total > 0`
宿主提示「下一步:灌数 → 打开模块」与此一致。
---
## 3. 首次接入流程
```text
宇恒 ID + 名称
→ POST /api/v1/auth/agent/register
→ pending
→ 管理员:角色「生成发布」+ 启用(可访问模块可留空)
→ 换票后即可自定 slug 生成/发布
```
`app_slugs` **默认留空即可自由发布**;仅在需要收紧范围时再配白名单。
---
## 4. 能力边界
### 允许
1. 首次登记(宇恒 ID + 名称)
2. 换票
3. 列出本账号可访问模块、读取已发布蓝图
4. 生成蓝图 / 生成新页面(可带 Excel / 截图 / HTML
5. 发布到建站平台(新建模块或向已有模块发布新页面;拿加密路径回执)
6. 导入数据 + 只读抽查列表
### 禁止
- 业务库查用户 / 权限表
- `row.create` / `row.update` / `row.delete`(除 import
- admin / audit 等管理接口
- 对已有模块默认 `mode=replace` 整站覆盖
- 访问未授权模块(仅当管理员配置了 `app_slugs` 白名单时才受限)
---
## 5. 角色与模块授权
推荐角色:**生成发布(`publisher`**
| 权限 | 用途 |
|------|------|
| 「发布模块」 | 发布模块 |
| `app.read` | 列模块 / 读蓝图 |
| `row.import` | 灌数 |
| `row.read` | 抽查 |
| `storage.write` / `storage.read` | 素材(建议) |
`app_slugs`**可选**。留空 = 不限制,智能体可随意发布自建模块;填写后才按白名单限制。
---
## 6. 宿主发布回执(需求)
宿主「AI 表格数据」应展示建站平台发布回执,而不是本地假数据:
| 表格项 | 含义 | 来自发布响应 |
|--------|------|----------------|
| 模块名称 | 业务显示名 | `module_name` |
| 发布方式 | 如立即发布上线 | `publish_style``immediate` |
| 访问地址 | 可打开的地址 | `access_url`(或宿主域名 + `access_path` |
| 发布时间 | 平台落库时间 | `published_at` |
| 状态 | 已发布 | `status` = `published` |
加密路径规则:
- 输入:租户 ID + 用户/智能体 ID + 模块 slug
- 输出:`access_path`(如 `m/ajzm1_...`),路径中**无明文用户 id / slug**
- 公开读蓝图:`GET /api/v1/public/m/{token}/blueprint`
---
## 7. 标准工作流
```text
宇恒ID + 名称
→ register → 管理员启用publisher模块白名单可选
→ 换票
→ GET /apps 【先选模块】
├─ 已有 → GET blueprint → generate【生成新页面】→ publish(add_pages|auto)
└─ 没有 → generate【完整蓝图】→ publish(create|auto)【新建模块】
→ 宿主用回执展示「AI 表格数据」(含加密访问路径)
→ import → 抽查 total > 0 → 打开模块
```
---
## 8. 验收清单
- [ ] 管理账号可在「模块管理」看到全部模块(含在建)
- [ ] register 传了宇恒 ID + 名称
- [ ] 角色为「生成发布」
- [ ] 发布前 `GET /api/v1/apps` 选目标(仅见本账号模块)
- [ ] 已有模块generate **生成新页面**再发布;无模块才新建
- [ ] 新页面 `id` / `route` 不与已有冲突
- [ ] `pages` ≥ 2含 list建议带 import
- [ ] publish 回执含 `access_path` / `access_url` / `published_at` / `status`
- [ ] 宿主表格用回执字段,不写死假地址
- [ ] generate → publish → import 成功,列表 `total > 0`
- [ ] 「打开模块」可见多页导航
- [ ] 不会去业务库查用户表

69
docs/角色说明.md Normal file
View File

@@ -0,0 +1,69 @@
# 角色说明
权限与角色均使用**中文命名**。分两类:公司成员角色(登录账号)、智能体角色(机器账号)。
## 〇、平台超级管理员与权限收窄
| 层级 | 谁 | 做什么 |
|------|----|--------|
| 权限模块 | **超级管理员** | 管理全部权限模块;决定每个公司**拥有哪些权限** |
| 公司一级 | **超级管理员** | 平台工作台:新建/改名公司、权限额度、管理员邀请 |
| 打开某公司 | **超级管理员** | 「管理该公司」打开该公司内部视图;**身份仍是超管**,写操作需两次确认 |
| 公司内日常 | **公司管理员** | 额度内分配角色/智能体;**成员管理**(如 demo 属于「演示公司」) |
| 硬边界 | 系统 | 公司账号不能分配或调用未授予的权限;控制台 Tab 按额度显隐 |
默认账号:**ljk_admin / ljk_admin**。
用法:平台工作台 → 某公司「管理该公司」→ 左侧出现角色/成员等 → 修改时两次确认。「返回平台工作台」回到公司列表。
开发演示数据:「演示公司」+ 账号 demo/demo123与超管 ljk_admin 是不同身份。
常用权限:读取/写入/发布模块;数据 CRUD 与导入导出;上传下载;审计;管理智能体;邀请成员;管理组织;数据同步。写入模块=保存在建草稿;发布模块=上线。
新建公司默认全量公司权限(可收窄至空)。创建时同步生成该公司**管理员**账号(**用户名随机全局唯一**、初始密码随机;明文仅创建时展示一次;登录后可自行「修改密码」)。成员管理也可直接「新建成员」(同样随机用户名)。
每家公司有全局唯一 **路径 slug**(如 `demo` → 约定对外 `/{slug}/...`)。平台工作台创建/改名时可填;演示公司固定为 `demo`。规则232 位、小写字母开头、仅 `a-z0-9-`;不可用 `api`/`admin`/`console` 等保留字。网关按 slug 分流属后续阶段。
## 一、公司成员角色
给真人登录账号用,存在用户表 / JWT 的 `role` 字段。
| 角色 | 能做什么 | 不能做什么 |
|------|----------|------------|
| **管理员** | 公司顶级权限:发布模块、管智能体/角色/组织/邀请、**数据同步**、全量数据 CRUD | — |
| **编辑** | 读写业务数据、导入导出、保存草稿(写入模块) | 发布、管组织/邀请/同步、删行 |
| **只读** | 看模块与数据、导出、下文件、看审计 | 任何写入、发布、管理类操作 |
| **待加入** | 仅能注册后等待;需邀请码加入公司 | 一切业务能力 |
| **智能体** | 不按上表套权限,而按所绑智能体角色的权限列表 | 不能配置「数据同步」(无该权限) |
邀请成员时可选项一般为:**编辑** / **只读** / **管理员**
## 二、智能体角色(默认)
给 AI / 宿主程序用的服务账号,在「角色管理」里配置,编码与名称均为中文。
| 角色 | 适合场景 | 主要权限 |
|------|----------|----------|
| **生成发布** | 建站智能体最低可用集:生成蓝图并发布、导入样例数据 | 读取/发布模块、查询/导入数据、上传下载 |
| **只读** | 只查询、导出的助手 | 读取模块、查询/导出数据、下载、审计 |
| **读写** | 日常维护业务数据,不发布新模块 | 读写模块配置、增改查导入导出、文件 |
| **运维** | 需要删数据、全量行操作与发布的运维机器人 | 含发布、删除及上列大部分能力 |
可在「角色管理」自定义角色并勾选中文权限;**不要**把「数据同步」赋给智能体(该权限仅管理员角色自带)。
## 三、与旧英文码
| 旧码 | 现中文 |
|------|--------|
| platform_admin / super_admin | 超级管理员 |
| owner | 管理员 |
| editor成员 | 编辑 |
| viewer成员 | 只读 |
| pending | 待加入 |
| agent | 智能体 |
| publisher | 生成发布 |
| editor智能体角色 | 读写 |
| viewer智能体角色 | 只读 |
| operator | 运维 |
读写库与鉴权时会自动把旧英文码归一成中文;启动时默认智能体角色编码会尽量升级为中文。