Files
workspace/code/g3soft-libs/docs/content/api/getting-started.md
T
2026-10-12 22:25:41 +08:00

7.2 KiB
Raw Blame History

title, description, pageClass
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 的错误拦截分支来处理业务失败。

统一响应格式

所有接口返回同一个外壳:

{
  "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 —— 通常已经说清是哪个参数错了。

鉴权

登录

curl -X POST http://localhost:8088/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"orgid":"G3HD","userid":"admin","password":"******"}'
{
  "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>

机构码不用在业务接口里传。 token 里带着 orgId,服务端据此把请求路由到该机构自己的数据库,前端只管带 token。

token 默认 24 小时过期(erp.auth.jwt-expiration)。同一账号再次登录会让旧 token 失效并返回 40101。

最小可跑示例

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):

{
  "code": 0,
  "message": "操作成功",
  "data": "1873456789012345678"
}

原因是雪花 ID 是 64 位,超过 JS 的 Number.MAX_SAFE_INTEGER(2⁵³−1),按数字解析会静默丢精度。所以 data.nextid、行里的各种 id 一律当 string 处理,不要 Number() / parseInt(),也不要参与算术。

接口一览

  • 数据查询 —— 数据查询:/data/describe、/data/loaddata、/data/page、/data/loaddatabysql、/data/nextid、/data/nextcode
  • 数据写入 —— 数据写入:/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 被放行),所以不能用 <img src="..."> 直接访问,需要按 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 基本能直接告诉你哪个参数不对。