重构: base 包资源核心类型迁移到 req/resx,新增 res_watcher/res_api_param
base/resource.go、resource_hooks.go、resource_test.go、query_field.go 删除,ResField 等具体实现搬到 req/resx(见 res_field.go 里的类型别名)。res_listener.go 替换成 res_watcher.go,对应资源变更监听概念改名。新增 res_api_param.go 及配套测试(ResApi 参数建模,给 MCP tool 的 JSON Schema 生成用)。
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"name": "res-query",
|
||||
"description": "Query records with filters, joins, aggregates, ordering, pagination, and recursive tree traversal.\rAll relations are SQL JOINs within a single query; the main resource is the primary table.\rPrefer JOIN over multiple queries: when the needed data spans several resources, combine same-connection resources into one query using relations, then make separate queries only for resources on different connections.\rWRONG: 3 queries — User → UserRole → Role, even though User and UserRole share the same connection.\rRIGHT: 1 query joining User + UserRole (same connection), then 1 separate query for Role (different connection).\rIMPORTANT:\r(1) Never guess field names — only use codes from res-schema results in this conversation.\r(2) Call res-schema first if the resource schema has not been fetched yet.\r(3) Use recursive to traverse all descendants of hierarchical data (e.g. categories, menus, org charts); returns a flat list, not a nested tree.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"conditions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/properties/conditions"
|
||||
},
|
||||
"title": "子条件",
|
||||
"description": "子条件节点,递归结构"
|
||||
},
|
||||
"exprs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"title": "字段编码",
|
||||
"description": "必须来自 res-schema 返回的字段列表,禁止猜测字段名"
|
||||
},
|
||||
"fieldResource": {
|
||||
"type": "string",
|
||||
"title": "所属资源",
|
||||
"description": "字段所属的表标识,取值为主资源编码或 relations[].code"
|
||||
},
|
||||
"fieldSqlFunc": {
|
||||
"type": "string",
|
||||
"title": "列SQL函数",
|
||||
"description": "对列应用 SQL 函数后再参与比较,拼接为 {func}({fieldResource}.{field}) {operator} {token}。\r\n支持 SQL-92 / SQL:1999 / SQL:2003 标准函数及数据库方言函数,如 DATE、UPPER、ROUND。\r\n需要第二参数的函数(如 ROUND、CAST)配合 fieldSqlFuncParam 使用。"
|
||||
},
|
||||
"fieldSqlFuncParam": {
|
||||
"type": "string",
|
||||
"title": "列SQL函数参数",
|
||||
"description": "fieldSqlFunc 的附加参数,按函数类型用途不同:\r\r通用函数(有 param 时):{func}({fieldResource}.{field}, {fieldSqlFuncParam}) {operator} {token}\r 示例:ROUND(col, 2) → fieldSqlFuncParam=\"2\"\r SUBSTRING(col, 1, 10) → fieldSqlFuncParam=\"1, 10\"\r\rJSON 函数(param 为 JSON 路径表达式):\r json_member_of(token 是否是列 JSON 数组的成员):\r 无 param:JSON_CONTAINS({token}, JSON_ARRAY({field}))\r 有 param:JSON_CONTAINS({fieldSqlFuncParam}, JSON_ARRAY({field}))\r json_contains(列 JSON 是否包含 token):\r 无 param:JSON_CONTAINS({field}, JSON_ARRAY({token}))\r 有 param:JSON_CONTAINS({fieldSqlFuncParam}, JSON_ARRAY({token}))"
|
||||
},
|
||||
"ignoreEmptyParam": {
|
||||
"type": "boolean",
|
||||
"title": "忽略空参数",
|
||||
"description": "当 tokenType=param 且请求参数值为空(空字符串或 null)时,跳过该条件不生成 SQL。用于实现可选过滤项",
|
||||
"default": false
|
||||
},
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"title": "运算符",
|
||||
"description": "标准 SQL 比较运算符:\r\n= / != / > / >= / < / <=\r\nLIKE:模糊匹配(%keyword%)\r\nIN:包含,token 逗号分隔多值\r\nIS NULL / IS NOT NULL:空值判断,无需 token\r\nREGEXP:正则匹配",
|
||||
"default": "="
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"title": "比较值",
|
||||
"description": "比较目标,内容由 tokenType 决定。operator 为 IS NULL/IS NOT NULL 时可省略"
|
||||
},
|
||||
"tokenType": {
|
||||
"type": "string",
|
||||
"title": "比较值类型",
|
||||
"description": "先选类型再填 token:\r\nstring:token 填字面量如 '2024-01-01'\r\nfunc:token 填内置函数名 UserID|UserUuid|UserRolesUuid|UserPlatform|UserSaaS\r\nparam:token 填请求参数路径如 body.status\r\nsql:token 填原始SQL如 CURRENT_DATE",
|
||||
"default": "string",
|
||||
"enum": [
|
||||
"string",
|
||||
"func"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "条件项",
|
||||
"description": "单条比较表达式,生成 [{fieldSqlFunc}(]{fieldResource}.{field}[)] {operator} {token} 片段",
|
||||
"required": [
|
||||
"field",
|
||||
"operator"
|
||||
]
|
||||
},
|
||||
"title": "条件表达式列表",
|
||||
"description": "当前层的叶子比较列表,每项生成一个 {fieldResource}.{field} {operator} {token} 片段,同层多项以 type 连接"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "逻辑连接符",
|
||||
"description": "当前节点的逻辑运算符,将 exprs 各项与 children 各组以 AND 或 OR 连接",
|
||||
"default": "and",
|
||||
"enum": [
|
||||
"and",
|
||||
"or"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "树形查询条件",
|
||||
"description": "WHERE 条件树。\rexprs 每项生成一段比较:[{fieldSqlFunc}(]{fieldResource}.{field}[)] {operator} {token},同层多项以 type(and/or) 连接;\rchildren 为同结构子节点,递归嵌套实现复杂过滤。",
|
||||
"required": [
|
||||
"exprs",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"title": "别名",
|
||||
"description": "输出字段别名,对应 SQL AS。isExpr=true 时必填;普通字段在多表 JOIN 存在同名冲突时填写,其余情况默认使用 code 值"
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"title": "字段编码",
|
||||
"description": "两种互斥用法,每个对象只能选其一:\r1. 普通字段编码,只填字段名本身如 amount,不含资源前缀,所属表由 codeResource 单独指定,isExpr 保持默认 false\r2. 原始 SQL 表达式(如 COUNT(*) / SUM(User.amount)),可含 {Resource}.{field} 引用,必须同时设置 isExpr = true 且 alias 必填\r注意:SELECT 中混入聚合表达式与非聚合字段时,需配合 groupBy 使用,否则结果不符合预期\r重要:字段编码必须来自 res-schema 返回的字段列表,禁止猜测或推断字段名"
|
||||
},
|
||||
"codeResource": {
|
||||
"type": "string",
|
||||
"title": "所属资源",
|
||||
"description": "字段所属的表标识,取值为主资源编码或 relations[].code"
|
||||
},
|
||||
"dataType": {
|
||||
"type": "string",
|
||||
"title": "数据类型",
|
||||
"description": "指定字段的返回数据类型,影响序列化方式;不填则由资源字段定义决定",
|
||||
"default": "string",
|
||||
"enum": [
|
||||
"string",
|
||||
"bool",
|
||||
"number",
|
||||
"array",
|
||||
"json",
|
||||
"integer",
|
||||
"float"
|
||||
]
|
||||
},
|
||||
"isExpr": {
|
||||
"type": "boolean",
|
||||
"title": "是否表达式",
|
||||
"description": "设为 true 时,code 内容作为原始 SQL 表达式拼入 SELECT(如聚合函数 COUNT(*) / SUM(amount)),而非普通字段编码。isExpr=true 时 alias 为必填项",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"title": "资源字段",
|
||||
"description": "结构化字段,支持指定所属表、输出别名、聚合表达式及数据类型",
|
||||
"required": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
"title": "查询字段",
|
||||
"description": "SELECT 字段列表,不传则返回所有字段(仅主资源字段,不含 JOIN 表)。\r使用 relations 时,JOIN 表的字段不会自动出现在结果中,必须在 fields 里显式列出需要的 JOIN 表字段,否则 JOIN 无意义。\r普通字段:code 填字段名,codeResource 指定所属表;聚合/表达式:isExpr=true,code 填 SQL 表达式,alias 必填。\r仅查询聚合结果时,fields 只传聚合字段,不要混入普通字段,除非同时提供 groupBy。\r重要:所有字段编码必须先通过 res-schema 确认,禁止凭名称语义猜测"
|
||||
},
|
||||
"groupBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"title": "字段编码",
|
||||
"description": "格式必须为 {Resource}.{field},如 User.status。\r{Resource} 取值为主资源编码或 relations[].code"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expr": {
|
||||
"type": "string",
|
||||
"title": "表达式",
|
||||
"description": "type=field 时填 {Resource}.{field}(如 User.status)\rtype=sql 时填原始 SQL 表达式(如 DATE(created_at))"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "表达式类型",
|
||||
"description": "field:按资源字段分组,expr 填 {Resource}.{field},如 User.status\rsql:按原始 SQL 表达式分组,expr 填任意合法 SQL,如 DATE(created_at)",
|
||||
"default": "field",
|
||||
"enum": [
|
||||
"field",
|
||||
"sql"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "分组表达式",
|
||||
"description": "结构化分组项,type=field 用于资源字段,type=sql 用于原始 SQL 表达式",
|
||||
"required": [
|
||||
"type",
|
||||
"expr"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"title": "分组规则",
|
||||
"description": "GROUP BY 列表,配合 fields 中的聚合表达式使用。\r字段必须带资源前缀({Resource}.{field});聚合函数(COUNT / SUM 等)放在 fields 中,不属于此处"
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"title": "每页条数",
|
||||
"description": "返回的最大记录数",
|
||||
"default": 30
|
||||
},
|
||||
"offset": {
|
||||
"type": "integer",
|
||||
"title": "偏移量",
|
||||
"description": "跳过的记录数",
|
||||
"default": 0
|
||||
},
|
||||
"orderBy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"title": "字段编码",
|
||||
"description": "格式必须为 {Resource}.{field},如 User.created_at,默认升序(ASC)。\r需要降序或使用 SQL 表达式时使用对象形式"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"direction": {
|
||||
"type": "string",
|
||||
"title": "排序方向",
|
||||
"default": "asc",
|
||||
"enum": [
|
||||
"asc",
|
||||
"desc"
|
||||
]
|
||||
},
|
||||
"expr": {
|
||||
"type": "string",
|
||||
"title": "表达式",
|
||||
"description": "type=field 时填 {Resource}.{field}(如 User.created_at);type=sql 时填原始 SQL 表达式(如 COUNT(*) / DATE(created_at))"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "表达式类型",
|
||||
"description": "field:按资源字段排序,expr 填 {Resource}.{field},如 User.created_at\rsql:按原始 SQL 表达式排序,expr 填任意合法 SQL,如 COUNT(*) / DATE(created_at)",
|
||||
"default": "field",
|
||||
"enum": [
|
||||
"field",
|
||||
"sql"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "排序表达式",
|
||||
"description": "结构化排序项,type=field 用于资源字段,type=sql 用于原始 SQL 表达式",
|
||||
"required": [
|
||||
"type",
|
||||
"expr"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"title": "排序规则",
|
||||
"description": "ORDER BY 列表,多项按顺序依次应用。\r字段必须带资源前缀({Resource}.{field});需要降序或 SQL 表达式排序时使用对象形式"
|
||||
},
|
||||
"relations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actuallyField": {
|
||||
"type": "string",
|
||||
"title": "实际资源字段",
|
||||
"description": "目标资源(code)参与 ON 条件的字段"
|
||||
},
|
||||
"actuallyResource": {
|
||||
"type": "string",
|
||||
"title": "实际资源",
|
||||
"description": "被关联的目标资源编码(相当于 SQL 表名)。JOIN 拼接为:{type} JOIN {actuallyResource} AS {code} ON {code}.{actuallyField} = {relationResource}.{relationField}"
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"title": "关联标识",
|
||||
"description": "JOIN 表别名(AS code),同时作为 fields/conditions/groupBy/orderBy 中 {Resource} 的取值。\r通常与 actuallyResource 保持一致;仅当多个关联引用同一资源导致冲突时,才需要重命名"
|
||||
},
|
||||
"conditions": {
|
||||
"$ref": "#/properties/conditions",
|
||||
"description": "该 JOIN 的附加过滤条件,作用于 JOIN ON 或主查询 WHERE,结构与顶层 conditions 相同"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "关联名称",
|
||||
"description": "关联的显示名称"
|
||||
},
|
||||
"relationField": {
|
||||
"type": "string",
|
||||
"title": "被关联资源字段",
|
||||
"description": "ON 条件对端资源的字段"
|
||||
},
|
||||
"relationResource": {
|
||||
"type": "string",
|
||||
"title": "被关联资源",
|
||||
"description": "ON 条件对端的资源编码(通常为主资源或父关联资源)"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "关联类型",
|
||||
"description": "SQL JOIN 类型(仅支持同库连接,不可跨库):\rinner:内连接,只返回两表均匹配的行\rleft:左连接,保留主表所有行\rright:右连接,保留关联表所有行",
|
||||
"enum": [
|
||||
"inner",
|
||||
"left",
|
||||
"right"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"actuallyField",
|
||||
"actuallyResource",
|
||||
"code",
|
||||
"relationField",
|
||||
"relationResource",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"title": "资源关联",
|
||||
"description": "SQL JOIN 列表,所有关联在同一条 SQL 中执行。\r关联资源的字段通过 code(表别名)在 fields / conditions / groupBy / orderBy 中引用。\r重要:所有关联资源必须与主资源在同一数据库连接。res-schema 返回的资源名称后面标有连接标识(如 · default / · service-support),建立 JOIN 前必须确认主资源与所有关联资源的连接标识完全一致,不一致则不可使用 JOIN,需分次查询后在应用层合并。"
|
||||
},
|
||||
"resource": {
|
||||
"type": "string",
|
||||
"title": "主资源",
|
||||
"description": "主资源编码(resource),作为本次查询的主表,relations 中的关联资源均以此为基础 JOIN"
|
||||
},
|
||||
"recursive": {
|
||||
"type": "object",
|
||||
"title": "递归查询",
|
||||
"description": "树形/层级数据的递归查询配置。配置后会以 pField=root 的记录为根节点,向下递归遍历所有子孙节点,返回平铺的记录列表(非嵌套树)。",
|
||||
"properties": {
|
||||
"pField": {
|
||||
"type": "string",
|
||||
"title": "父节点字段",
|
||||
"description": "存储父节点 ID 的字段编码,格式为 {Resource}.{field}。{Resource} 取值为主资源编码或 relations[].code,如 Category.pid"
|
||||
},
|
||||
"cField": {
|
||||
"type": "string",
|
||||
"title": "当前节点字段",
|
||||
"description": "存储自身 ID 的字段编码,格式为 {Resource}.{field}。{Resource} 取值为主资源编码或 relations[].code,如 Category.id"
|
||||
},
|
||||
"root": {
|
||||
"type": "string",
|
||||
"title": "根节点值",
|
||||
"description": "递归起点,pField 等于此值的记录作为根节点,通常为 0 或空字符串"
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"title": "递归深度",
|
||||
"description": "最大递归层数,防止循环引用导致无限递归,不传则不限制"
|
||||
}
|
||||
},
|
||||
"required": ["pField", "cField", "root"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fields",
|
||||
"resource"
|
||||
]
|
||||
},
|
||||
"annotations": {}
|
||||
},
|
||||
{
|
||||
"name": "res-create",
|
||||
"description": "Create one or multiple new records for the specified resource. Returns all fields of the created record(s).\rCall res-schema first to get the field list before building the data object.\rIMPORTANT: Never guess field names — only use codes from res-schema results.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resource": {
|
||||
"type": "string",
|
||||
"title": "主资源",
|
||||
"description": "目标资源编码,数据将写入该资源对应的表"
|
||||
},
|
||||
"data": {
|
||||
"title": "创建数据",
|
||||
"description": "单条记录传对象,批量创建传对象数组。\rkey 为字段编码(必须来自 res-schema 返回的字段列表,禁止猜测),value 为写入值。\r有默认值的字段可以不传,系统会自动填充。\r批量创建(array)时,所有记录必须包含完全相同的字段集合,不可部分记录有某字段而其他记录没有。\r系统保留字段禁止传入,包括:id、created_at、updated_at、created_user、owned_user、enabled 及其他由系统自动维护的字段",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "单条记录",
|
||||
"description": "创建单条记录,key 为字段编码,value 为字段值",
|
||||
"additionalProperties": true
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"title": "批量记录",
|
||||
"description": "批量创建,每个元素为一条记录对象",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["resource", "data"]
|
||||
},
|
||||
"annotations": {}
|
||||
},
|
||||
{
|
||||
"name": "res-update",
|
||||
"description": "Update records by their IDs. Returns only the updated fields of the modified records.\rBefore calling this tool, ALWAYS use res-query to retrieve and show the user the records that will be affected. Only call res-update after the user has reviewed and confirmed.\rPass __confirm__: true when the user has approved the update.\rIMPORTANT: Never guess field names — only use codes from res-schema results.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resource": {
|
||||
"type": "string",
|
||||
"title": "主资源",
|
||||
"description": "目标资源编码"
|
||||
},
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"title": "记录ID列表",
|
||||
"description": "必填,指定要更新的记录 ID 列表。必须通过 res-query 预先查询获得,禁止手动构造。",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"title": "更新数据",
|
||||
"description": "只传需要修改的字段,未传字段保持不变。\rkey 为字段编码(必须来自 res-schema 返回的字段列表,禁止猜测),value 为新值。\r系统保留字段禁止传入,包括:id、created_at、updated_at、created_user、owned_user、enabled 及其他由系统自动维护的字段",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"__confirm__": {
|
||||
"type": "boolean",
|
||||
"title": "确认执行",
|
||||
"description": "首次调用不传。仅当服务端返回需要确认的提示时,重新调用并传入 true 以执行更新。不可主动传入,否则将跳过安全确认步骤。",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["resource", "ids", "data"]
|
||||
},
|
||||
"annotations": {}
|
||||
},
|
||||
{
|
||||
"name": "res-delete",
|
||||
"description": "Hard-delete records by ID (physical removal, unrecoverable).\rWARNING: Prefer soft delete — use res-update to set deleted_at=CURRENT_TIMESTAMP and enabled=0 instead.\rOnly call res-delete when the user explicitly requests permanent/physical deletion.\rBefore calling, ALWAYS use res-query to show the user the affected records and obtain confirmation.\rPass __confirm__: true after the user has confirmed.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resource": {
|
||||
"type": "string",
|
||||
"title": "主资源",
|
||||
"description": "目标资源编码"
|
||||
},
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"title": "记录ID列表",
|
||||
"description": "必填,指定要删除的记录 ID 列表。必须通过 res-query 预先查询获得,禁止手动构造。",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"__confirm__": {
|
||||
"type": "boolean",
|
||||
"title": "确认执行",
|
||||
"description": "首次调用不传。仅当服务端返回需要确认的提示时,重新调用并传入 true 以执行删除。不可主动传入,否则将跳过安全确认步骤。",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["resource", "ids"]
|
||||
},
|
||||
"annotations": {}
|
||||
},
|
||||
{
|
||||
"name": "res-schema",
|
||||
"description": "Look up available resources and their field definitions. Without keywords: lists all resources. With keywords: returns matched resources with their field list, types, and constraints. Keywords match against resource code, name, or table name; supports Chinese fuzzy matching; use | to separate multiple terms (e.g. \"用户|权限\"). Call this before using res-query / res-update / res-delete if the resource schema is not already in the current conversation.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keywords": {
|
||||
"type": "string",
|
||||
"title": "查找关键字",
|
||||
"description": "搜索关键词,用于筛选资源列表。支持多个关键词,用 | 分隔。\r对每条资源的编码(code)、名称(name)、数据表名(table)进行匹配,\r支持中文分词模糊匹配,结果按匹配相关度从高到低排序。\r示例:\"用户\" 或 \"用户|权限\""
|
||||
}
|
||||
}
|
||||
},
|
||||
"annotations": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user