--- title: 数据查询 description: describe / loaddata / page / loaddatabysql / nextid / nextcode pageClass: g3-api --- # 数据查询 六个只读接口。除了 `nextid`(纯取号,不碰数据库),其余都会落到**当前机构自己的数据库**上执行。 共用规则: - **表名、视图名、列名都会先过标识符校验**(只允许 `[A-Za-z_][A-Za-z0-9_]*`,可带限定名如 `dbo.v_x`),不会做字符串拼接 —— 所以别指望能塞入非法标识符。 - `search_condition` 是**原样拼进 `WHERE` 的 SQL 条件片段**;拼好的整条 SQL 之后会过只读校验(禁分号、禁注释、禁写关键字),所以它只能是布尔表达式。 - `order_by` 由后端校验并加 `[]`:支持 `字段 [ASC|DESC]`,或 `CASE ... END` 表达式,多列用逗号分隔。 - 返回的行是普通对象,键是**库里的原始列名**。 ## `POST` `/data/describe` 探测表或视图的列结构,用于动态列、字段同步。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `table` | string | 是 | 表或视图名 | ```json { "table": "v_b_othercompany" } ``` ```json { "code": 0, "message": "操作成功", "data": [ { "name": "b_id", "type": "varchar", "nullable": false, "size": 32 }, { "name": "b_name", "type": "nvarchar", "nullable": true, "size": 128 } ] } ``` `type` 是数据库原生类型名(`varchar` / `nvarchar` / `decimal` / `datetime2` / `rowversion` …)。表不存在或没有可识别字段时返回 `1000`。 ## `POST` `/data/loaddata` 按视图 + 条件 + 排序 + 字段列表查询。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `view_name` | string | 是 | 表或视图名 | | `search_columns` | string[] | | 只取这些列;省略等价于 `*` | | `search_condition` | string | | `WHERE` 片段 | | `order_by` | string | | 排序,多列逗号分隔 | ```json { "view_name": "v_b_othercompany", "search_columns": ["b_id", "b_name", "b_canuse"], "search_condition": "b_canuse = 1", "order_by": "b_id ASC, b_name DESC" } ``` ```json { "code": 0, "message": "操作成功", "data": [{ "b_id": "C001", "b_name": "XX 公司", "b_canuse": 1 }] } ``` > **没有行数上限。** `loaddata` 会把命中的行全部返回;列表页请用 `/data/page`。它的典型用途是取一个小字典(下拉选项)或按主键取单行。 ## `POST` `/data/page` 分页查询。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `view_name` | string | 是 | 表或视图名 | | `order_by` | string | 是 | 分页必须有序(SQL Server 的 `OFFSET / FETCH` 要求) | | `search_columns` | string[] | | 只取这些列 | | `search_condition` | string | | `WHERE` 片段 | | `page_no` | number | | 默认 `1`,从 1 开始 | | `page_size` | number | | 默认 `20` | ```json { "view_name": "v_b_othercompany", "search_condition": "b_canuse = 1", "order_by": "b_id ASC", "page_no": 2, "page_size": 20 } ``` ```json { "code": 0, "message": "操作成功", "data": { "rows": [{ "b_id": "C021", "b_name": "XX 公司" }], "row_count": 20, "total": 137, "page_no": 2, "page_size": 20 } } ``` | 字段 | 说明 | |---|---| | `rows` | 当前页数据 | | `row_count` | **当前页**的行数(等于 `rows.length`) | | `total` | 命中总行数 | | `page_no` / `page_size` | 回显请求里的分页参数 | > `row_count` 不是总行数,**总行数是 `total`**。算总页数用 `total`,别用 `row_count`。 > > `page_no` 或 `page_size` 小于 1、或不是整数,都会得到 `1000`(`page_no 必须是大于 0 的整数`)。 翻到最后一页之后(`page_no` 超出范围)`rows` 为空,但 `total` 仍然正确 —— 所以判断「没数据」和「翻过了头」要靠 `total`。 ## `POST` `/data/loaddatabysql` 配置表达不出来的复杂只读查询走这里。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `sql` | string | 是 | 必须以 `SELECT` 或 `WITH` 开头 | ```json { "sql": "SELECT TOP 10 b_id, b_name FROM v_b_othercompany ORDER BY b_id" } ``` ```json { "code": 0, "message": "操作成功", "data": [{ "b_id": "C001", "b_name": "XX 公司" }] } ``` 被拒的情况(都是 `1000`):不是 `SELECT` / `WITH` 开头、包含 `;`、包含注释(`--` 或 `/* */`)、出现 `insert` `update` `delete` `drop` `truncate` `alter` `exec` `grant` 等关键字。 > 判定前会先把**字符串字面量整体掩掉**,所以在 `WHERE b_name = 'insert'` 这类写法里,字面量里的关键字不会误判。反过来也说明:不能在字面量之外藏写操作。 ## `POST` `/data/nextid` 取雪花 ID。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `count` | number | | 默认 `1`;`<= 1` 时 `data` 是单个 id,否则是数组 | 请求体**可以整个省略** —— 不带 body 或传 `{}` 是等价的,`count` 都取默认的 `1`: ```json {} ``` ```json { "code": 0, "message": "操作成功", "data": "1873456789012345678" } ``` `count: 3` 时: ```json { "code": 0, "message": "操作成功", "data": [ "1873456789012345678", "1873456789012345679", "1873456789012345680" ] } ``` > 返回的是**字符串**(见 [快速开始](/api/getting-started) 里的序列化约定),不要转成数字。 ## `POST` `/data/nextcode` 按模块配置的自动编码规则取业务编码(规则来自 `s_autocode`:前缀、日期格式、序号位数、分隔符、重置周期等)。 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `moduleId` | string | 是 | 模块编码(注意是 camelCase 的 `moduleId`) | | `count` | number | | 默认 `1`;`<= 1` 时返回单个字符串,否则返回数组 | ```json { "moduleId": "othercompany", "count": 1 } ``` ```json { "code": 0, "message": "操作成功", "data": "HT20261012001" } ``` 行为要点: - **会占用号段** —— 服务端取号后立即把序号写回 `s_autocode`,所以连续调用得到的是不同编码。别拿它做「探测一下看看格式」的试调,试出来的号就废了。 - 生成时会**检查目标表里该编码是否已存在**,已被占用(比如人工插过一条)就自动往后顺延,所以不一定严格等于「当前序号 + 1」。 - 模块没有配置启用的编码规则、或没配保存表 → `1000`(`当前模块未配置启用的自动编码规则` / `模块未配置保存表,无法生成自动编号`)。 - 生成的编码超过目标字段长度 → `1000`(`自动编号长度超过目标字段长度`)。