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

6.7 KiB
Raw Blame History

title, description, pageClass
title description pageClass
数据写入 saveobjt / delete,含 $original、$expr 与 guards g3-api

数据写入

POST /data/saveobjt

请求体是一个数组:一次可提交多个表的增、改、删,全部在同一个事务里 —— 任何一项失败整批回滚,不会留下半套数据。

请求字段

字段 类型 必填 说明
table string 是 保存目标表
key_field string 是 主键列名,用于定位要改/删的行
updates object[] 要更新的行,主键值必须在行里
inserts object[] 要新增的行
deletes object[] 要删除的行(用 key_field 定位)
delete_target string 声明 deletes 走哪条路径:module(按模块配置联动删子表,需 moduleCode)或 direct(直接删,需 table / key_field)
guards object 前置条件,见下
[
  {
    "table": "b_othercompany",
    "key_field": "b_id",
    "updates": [{ "b_id": "C001", "b_name": "新名字" }],
    "inserts": [{ "b_id": "C002", "b_name": "新公司" }],
    "deletes": [{ "b_id": "C003" }]
  }
]

成功时 data 为 null,message 为 保存成功:

{
  "code": 0,
  "message": "保存成功",
  "data": null
}

没传 delete_target 时,deletes 是逐行按主键直删,不会触发模块配置里的联动删除。需要联动就把 delete_target 声明成 module 并带上 moduleCode。

行内的两个保留键

键名以 $ 开头的不会被当成业务列:

键 用途 说明
$original 乐观锁:声明「我改动之前看到的值」 这些字段只进 WHERE 不进 SET;对不上就是 2003。值为 null 时生成 IS NULL
$expr 字段自增:{"$expr": "b_amount + 5"} 只支持「本表列 ± 数值常量」,列必须是数值类型
{
  "b_id": "C001",
  "b_amount": { "$expr": "b_amount + 5" },
  "$original": { "b_name": "旧名字", "b_row_version": 3 }
}
  • $original 里带 b_row_version 时走整行版本锁:服务端校验通过后自动 +1,请求里同名的普通值不生效(避免 SET 里出现两次该列)。
  • inserts 的行不允许带 $original / $expr —— 新增的行没有原值可比,带了会直接报 1000。
  • $expr 的字段不存在、不可写、不是数值类型、格式不对,都会在任何一行写入之前报 1000(这类参数错误不会等到写到第 N 行才暴露)。

guards:写入前置条件

让「判断」和「写入」发生在同一条 SQL 里,避免先查后写的竞态。

{
  "table": "b_othercompany",
  "key_field": "b_id",
  "updates": [{ "b_id": "C001", "b_name": "新名字" }],
  "guards": {
    "updates": [
      {
        "condition": "{b_state} = 'draft'",
        "refs": ["b_othercompany"],
        "message": "只有草稿状态才能改名",
        "retryable": false
      }
    ]
  }
}

guards 下的三个分节键按动作区分 —— inserts / updates / deletes(注意是 deletes):

字段 类型 必填 说明
condition string 是 SQL 布尔表达式;{列名} 是受控占位符,替换为 t.[列名];不允许注释
refs string[] 声明引用的表,只进日志
message string 条件不成立时给用户看的文案
retryable boolean 默认 true;false 时前端文案不追加「请刷新后重试」

条件不成立时返回 2002,message 就是上面写的 message,data 带 table / action / index。

两条互斥规则(同时用会报 1000):

  • guards.deletes 与 delete_target 互斥;
  • guards.deletes 与 dependent_deletes 互斥。

原因是带条件的删除只能逐行唯一定位,而 delete_target / 依赖删除那两条旁路拿不到 guards —— 与其让条件被静默忽略,不如直接拒绝。

三个错误码怎么分

code 含义 典型原因 data 里的定位信息
2001 保存失败 字段不存在、类型不匹配、数据库约束冲突等 table / action / index / cause
2002 前置条件不成立 guards 里的条件没满足 table / action / index
2003 并发编辑冲突 $original 的原值已被别人改过、b_row_version 不匹配 table / action / index / fields

2003 的正确处理不是重试,而是:重新拉取该行,用 data.fields 标出哪些字段已被他人修改,让用户决定覆盖还是放弃。

action 的取值是 update / insert / delete,index 是请求数组里的下标(从 0 开始)—— 批量保存失败时靠它定位到具体哪一行。

POST /data/delete

独立删除接口。和 saveobjt 的区别是它不进保存事务、也不涉及增改。

参数 类型 必填 说明
target string 是 module 或 direct,其它值报 1000
moduleCode string 是(target=module) 模块编码(s_module.b_id)
table string 是(target=direct) 表名
key_field string 是(target=direct) 主键列名
ids any[] 要删除的主键值;direct 下为空数组会直接返回 {}

按模块删除(会按模块配置联动删除子表):

{
  "target": "module",
  "moduleCode": "othercompany",
  "ids": ["C001", "C002"]
}

指定表直接删除:

{
  "target": "direct",
  "table": "b_othercompany",
  "key_field": "b_id",
  "ids": ["C001"]
}
{
  "code": 0,
  "message": "操作成功",
  "data": { "b_othercompany": 2 }
}

data 是 {表名: 实际删除行数} —— direct 只有一项;module 会带出所有被联动删除的表,所以可能有多项。整个删除同样在单个事务里完成。

target 必须显式声明。后端刻意不做「没传 moduleCode 就当直删」这种隐式分支,避免漏传参数时静默绕过模块规则。