---
title: 快速开始
description: 接口约定、鉴权与联调步骤
pageClass: g3-api
---
# 快速开始
后端提供的是一套**元数据驱动的通用数据接口**:前端不拼业务 SQL,只声明「查哪张视图 / 存哪张表 + 条件 + 字段」,由后端按 `s_module` / `s_field` 里的配置生成参数化 SQL 并执行。除登录外,所有接口共用同一套鉴权、机构数据源路由与统一响应格式。
## 基础约定
| 项 | 约定 |
|---|---|
| 基础路径 | `/api`(`server.servlet.context-path`),例如 `http://localhost:8088/api/data/page` |
| 方法 | **全部是 `POST`**;唯一的 `GET` 是文件静态访问 `/api/file/static/{fileName}` |
| 请求体 | `application/json`,参数是**一个对象或数组**,不是表单 |
| 编码 | UTF-8 |
| HTTP 状态码 | **始终是 200** —— 成败一律看响应体里的 `code`,不要用 HTTP 状态码判断 |
最后一条最容易踩:鉴权失败、参数错误、系统异常**都不会**返回 4xx / 5xx,而是 200 + `code != 0`。所以不要依赖 `axios` 的错误拦截分支来处理业务失败。
## 统一响应格式
所有接口返回同一个外壳:
```json
{
"code": 0,
"message": "操作成功",
"data": {}
}
```
| 字段 | 说明 |
|---|---|
| `code` | `0` 成功,非 0 见下表 |
| `message` | 可直接展示给用户的文案;出错时是原因 |
| `data` | 成功时的载荷;失败时一般为 `null` |
## 错误码
| code | 含义 | 触发场景 | 前端应对 |
|---|---|---|---|
| `0` | 成功 | —— | —— |
| `401` | 未登录 / 凭证失效 | 缺少或无效的 `Authorization`;账号密码错误 | 清 token,回登录页 |
| `40101` | 账号已在其他设备登录 | 同一账号在别处登录,本端会话被顶 | 清 token,提示后回登录页 |
| `1000` | 业务错误(默认) | 参数缺失或类型不对、标识符非法、只读 SQL 校验不通过、目标表没有可识别字段等 | 直接用 `message` 提示 |
| `2001` | 保存失败 | 写入时的数据库错误,`data` 带 `table` / `action` / `index` / `cause` | 提示 `message`;`data.index` 可定位到第几行 |
| `2002` | 保存前置条件不成立 | 请求声明的 guard 条件不满足,`data` 带 `table` / `action` / `index` | 直接用 `message`(那是写条件的人给的业务文案) |
| `2003` | 并发编辑冲突 | `$original` 原值或 `b_row_version` 不匹配,`data` 带 `fields` | 提示后**重新拉取该行**,标出 `fields` 里的字段 |
| `500` | 系统异常 | 未预期的异常 | 提示「系统异常,请稍后重试」 |
参数类错误都是 `1000`,`message` 形如 `view_name 不能为空`、`page_no 必须是大于 0 的整数`、`参数类型不正确`、`非法数据库标识符: xxx` —— 通常已经说清是哪个参数错了。
## 鉴权
### 登录
```bash
curl -X POST http://localhost:8088/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"orgid":"G3HD","userid":"admin","password":"******"}'
```
```json
{
"code": 0,
"message": "操作成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"orgid": "G3HD",
"user": { "id": "g3soft", "account": "g3soft", "name": "G3Soft" }
}
}
```
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `orgid` | string | 是 | 机构码,决定连哪个机构的数据库 |
| `userid` | string | 是 | 账号;字符串业务键方案下 `b_user.b_id` 就是账号本身 |
| `password` | string | | 与库里存的密码比对 |
机构码或账号为空 → `1000`(`机构码不能为空` / `用户ID不能为空`);账号、密码、机构任一对不上 → 统一 `401` + `机构码、账号或密码错误`(刻意不区分是哪一个错)。
### 带上凭证
除 `/api/auth/login` 与 `OPTIONS` 预检外,**所有接口都要带**:
```
Authorization: Bearer
```
> **机构码不用在业务接口里传。** token 里带着 `orgId`,服务端据此把请求路由到该机构自己的数据库,前端只管带 token。
>
> token 默认 24 小时过期(`erp.auth.jwt-expiration`)。同一账号再次登录会让旧 token 失效并返回 `40101`。
## 最小可跑示例
```js
const api = axios.create({ baseURL: '/api' })
api.interceptors.request.use((config) => {
const token = localStorage.getItem('g3erp:token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
// HTTP 200 也可能是业务失败,所以要在这里统一判 code
api.interceptors.response.use((res) => {
const { code, message } = res.data
if (code === 401 || code === 40101) {
localStorage.removeItem('g3erp:token')
location.href = '/login'
}
return res.data
})
// 分页查询
const { data } = await api.post('/data/page', {
view_name: 'v_b_othercompany',
search_columns: ['b_id', 'b_name'],
search_condition: 'b_canuse = 1',
order_by: 'b_id ASC',
page_no: 1,
page_size: 20,
})
// data.rows / data.total / data.page_no / data.page_size
```
## 一个必须知道的序列化约定
**所有长整型(`Long`)都以 JSON 字符串输出**(后端 `JacksonConfig` 给 `Long` / `long` 注册了 `ToStringSerializer`):
```json
{
"code": 0,
"message": "操作成功",
"data": "1873456789012345678"
}
```
原因是雪花 ID 是 64 位,超过 JS 的 `Number.MAX_SAFE_INTEGER`(2⁵³−1),按数字解析会**静默丢精度**。所以 `data.nextid`、行里的各种 id 一律当 `string` 处理,不要 `Number()` / `parseInt()`,也不要参与算术。
## 接口一览
- **数据查询** —— [数据查询](/api/data):`/data/describe`、`/data/loaddata`、`/data/page`、`/data/loaddatabysql`、`/data/nextid`、`/data/nextcode`
- **数据写入** —— [数据写入](/api/save):`/data/saveobjt`、`/data/delete`
- **鉴权** —— 本页:`/auth/login`
- **文件** —— 暂无专页:`/file/config`、`/file/upload`、`/file/upload-credential`、`/file/save-record`、`/file/delete`、`GET /file/static/{fileName}`
- **导出** —— 暂无专页:`/export/list`、`/export/count`、`/export/totals`(**返回文件流,不是 `ApiResponse`**)
> `/file/static/{fileName}` 同样受鉴权过滤(只有 `/auth/login` 被放行),所以不能用 `
` 直接访问,需要按 blob 取回再用 `URL.createObjectURL`。
## 联调步骤
1. 起后端:`cd server && ./mvnw spring-boot:run`(默认 8088);前端 dev server 的 `/api` 代理到它。
2. 调 `/api/auth/login` 拿 token,存进 `localStorage`(键名沿用组件库示例里的 `g3erp:token`)。
3. 用 `/api/data/describe` 看目标表或视图有哪些列,再调 `/api/data/page` 取数据。
4. 拿到 `code != 0` 先查上面的错误码表;`1000` 的 `message` 基本能直接告诉你哪个参数不对。