# 中台执行步骤与安全闸门

面向 go-zero（或等价中台）实现 `POST /api/v1/apps/{slug}:publish`。

## 1. 入口校验

1. 鉴权：用户已登录，具备 `app.admin` 或「创建应用」权限  
2. 限流：每租户每小时发布次数上限（建议 ≤ 20）  
3. Body 必须是完整 `AppBlueprint`  
4. 用 `app-blueprint.schema.json` 校验；失败直接 400  
5. `meta.slug` 与 path 中 slug 一致；租户内唯一  

## 2. 标识符白名单（防 SQL 注入）

仅允许匹配：

```text
^[a-z][a-z0-9_]{1,47}$
```

校验对象：`slug`、`entity.name/table`、所有 `field.name`、`index.name`  
拒绝：大小写混用、连字符、空格、引号、注释符、SQL 关键字作表名（建议黑名单：`select/drop/user/...`）

## 3. 分配存储（用户不可指定 DSN）

| storage.mode | 行为 |
|--------------|------|
| `schema_per_app` | 平台在共享实例创建 `schema_name`（可忽略 AI 填的名字，按 `app_{tenant}_{slug}` 重写） |
| `database_per_app` | 平台开通独立库，凭证写入 KMS/密钥服务 |

落库元数据示例：

```text
tenant_apps(app_id, tenant_id, slug, schema_name, engine, blueprint_json, status, created_at)
tenant_app_entities(...)
tenant_app_fields(...)
tenant_app_apis(...)
```

## 4. 生成并执行 DDL（参数化/白名单拼接）

伪代码原则：

- 只拼已经过白名单的标识符  
- 字段类型映射用固定字典，禁止把 AI 的 type 字符串直接塞进 SQL  

类型映射建议（Postgres）：

| blueprint type | SQL |
|----------------|-----|
| string | VARCHAR(n) |
| text | TEXT |
| int | INTEGER |
| bigint | BIGINT |
| decimal | NUMERIC(p,s) |
| boolean | BOOLEAN |
| date | DATE |
| datetime | TIMESTAMPTZ |
| enum | VARCHAR(n) + CHECK |
| json | JSONB |
| file_ref | VARCHAR(512) |

每个表强制附加系统列（即使蓝图未写）：

```sql
tenant_id  BIGINT NOT NULL,
created_by BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
```

`row_policies=tenant_isolated` → 所有查询自动 `AND tenant_id = :current_tenant`。

## 5. 注册动态 API

读取 `apis.resources[]`：

- 在网关/服务路由表注册 path  
- `operations` 决定开放方法  
- `allowed_filters/sorts` 写入配置；运行时拒绝未声明字段  

禁止：按请求参数动态选物理表名。  
允许：`app_slug + resource` → 元数据查出 `schema.table`。

## 6. 种子数据

若 `seed.import_excel=true`：

1. 从 `meta.source.excel_ref` 拉文件  
2. 按 `field.from_excel.column` 映射  
3. 行数 ≤ `seed.max_rows`  
4. 批量插入，单行失败记入 errors，不中断整单（或按策略 fail-fast）  

## 7. 发布事务与回滚

建议状态机：

```text
draft → validating → provisioning → importing → published
                 ↘ failed（保留草稿，DDL 尽量事务/可回滚）
```

失败时：

- schema 已建：标记 `failed`，提供 `DELETE app` 清理  
- 写审计：谁、何时、哪份 blueprint hash、结果  

## 8. 运行时读路径（列表页）

```text
Auth → 解析 tenant → 查 app 元数据 → 校验 resource
→ 校验 filter/sort 白名单 → 组装 SELECT
→ 强制 tenant_id 条件 → 分页返回
```

前端只读 blueprint 渲染，不信任客户端传来的「表名/SQL」。

## 9. 最小落地服务拆分

| 服务 | 职责 |
|------|------|
| `ai-generate` | 素材 → draft blueprint |
| `app-meta` | 蓝图存储、校验、发布状态 |
| `schema-runner` | DDL / 迁移 / 清理 |
| `dynamic-crud` | 统一 CRUD / import / export |
| `gateway` | 鉴权、限流、WAF、路由 |

首版可先 2 个进程：`ai-generate(FastAPI)` + `platform(go-zero 含 meta/runner/crud)`。
