Files
oracle-jump-query-public/references/legacy-skill-v1.5.8.md
T

693 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: oracle-jump-query
description: Oracle 跳板查询技能。通过中转服务查询远程 Oracle 数据库的元数据(存储过程源码、依赖、表结构、触发器、直接查表),连接数据库为只读,禁止任何写操作。当用户需要分析 Oracle 存储过程、查找依赖表、查询表结构、查看业务逻辑时触发。
---
# Oracle 跳板查询
> **版本:v1.5.8** · [更新日志](./CHANGELOG.md)
## 概述
通过中转服务(Transit Server)查询远程 Oracle 数据库服务器的存储过程元数据,无需开放数据库端口。
## 架构
```
AI Skill → HTTP → Transit Server (:6357) → WebSocket → Agent → Oracle
```
## 前置条件
1. 中转服务已部署并运行(IP: 47.97.171.89, Port: 6357)
2. Agent 已部署到数据库服务器并连接中转服务
3. 本地已安装 Python 依赖:`pip install requests`
## 配置
配置文件位于技能目录下:`D:\work\恒诺\长期支持\跳板查询\demo\ai-skill\config.json`
```json
{
"transit_url": "http://47.97.171.89:6357",
"server_id": "server-001",
"access_token": "",
"expires_at": ""
}
```
使用查询命令前先登录中转机:
```bash
python oracle_skill.py login <secretKey> [clientCode]
```
登录成功后只保存中转机签发的 `access_token` 和过期时间,不保存 BOS `secretKey`。`clientCode` 是客户服务器编号,非必填;不传时中转机会默认选择 BOS 返回的第一个可用 client。
同一个 `secretKey` 同时只允许一个设备在线。另一台设备重新登录后,当前设备的 token 会立即失效,需要重新执行 `login <secretKey> [clientCode]`。
## 调用方式
使用 `scripts/oracle_skill.py` 脚本:
```bash
python scripts/oracle_skill.py <command> [args]
```
### 可用命令
| 命令 | 说明 | 示例 |
|------|------|------|
| `analyze <schema> <proc>` | 完整分析存储过程 | `python oracle_skill.py analyze BOS M_RETAIL_SUBMIT` |
| `list <schema>` | 列出 schema 下所有存储过程 | `python oracle_skill.py list BOS` |
| `source <schema> <proc>` | 获取存储过程源码 | `python oracle_skill.py source BOS M_RETAIL_SUBMIT` |
| `deps <schema> <proc>` | 获取依赖(表/存储过程) | `python oracle_skill.py deps BOS M_RETAIL_SUBMIT` |
| `tables <schema> <proc>` | 获取相关表结构 | `python oracle_skill.py tables BOS M_RETAIL_SUBMIT` |
| `describe <schema> <table>` | 查询表结构(列、索引、行数) | `python oracle_skill.py describe bosnds3 xcx_so` |
| `discover [schema] [domain]` | 发现核心业务域(NL2SQL前置) | `python oracle_skill.py discover BOSNDS3 RETAIL` |
| `nl2sql [schema] [domain]` | 生成 NL2SQL Schema 字典 | `python oracle_skill.py nl2sql BOSNDS3 RETAIL` |
| `query <SQL>` | 执行 SELECT 查询,生产表必须带过滤条件和行数限制 | `python oracle_skill.py query "SELECT * FROM M_RETAIL WHERE BILLDATE=20260501 AND ROWNUM<=20"` |
| `perm <userId> <tableId> [col]` | 查询用户数据权限 | `python oracle_skill.py perm 940 12983` |
| `qperm <userId> <tableId> <SQL>` | 带权限过滤的查询 | `python oracle_skill.py qperm 940 12983 "SELECT * FROM M_OTHER_INOUT"` |
| `login <secretKey> [clientCode]` | 登录中转机 + 选择客户服务器,clientCode 非必填 | `python oracle_skill.py login mykey HENLO` |
| `logout` | 登出中转机并清除本地 token | `python oracle_skill.py logout` |
| `status` | 查看中转机登录状态 + 当前 client | `python oracle_skill.py status` |
| `switch <clientCode>` | 切换 client | `python oracle_skill.py switch OTHER_CLIENT` |
| `clients` | 获取当前用户可访问 client 及在线状态 | `python oracle_skill.py clients` |
| `servers` | 列出在线 Agent | `python oracle_skill.py servers` |
| `health` | 检查中转服务状态 | `python oracle_skill.py health` |
| `tablespace / tablespaces` | 查询表空间使用情况 | `python oracle_skill.py tablespace` |
### 交互模式
不传参数直接运行,进入交互模式:
```bash
python scripts/oracle_skill.py
```
## 生产查询限制
直接查询业务表时,必须带明确过滤条件,不要直接查询整表。生产环境数据量很大,`SELECT * FROM <table>` 这类无条件查询可能返回大量数据、拖慢 Agent 或影响数据库。
生成或执行 `query` / `qperm` SQL 时遵守:
- 必须有 `WHERE` 条件,优先使用日期、单号、门店、客户、状态、主键等业务过滤条件。
- 必须加行数限制,例如 `ROWNUM <= 20`;需要分页时分批查询。
- 不要为了“先看看数据”执行无条件整表查询。
- 统计查询也要限定业务范围,例如日期区间、单据状态、门店范围。
- 如果用户没有给出过滤条件,先追问条件,或先用 `describe` / `AD_COLUMN` / `AD_TABLE` 查询结构和字段含义。
推荐示例:
```sql
SELECT *
FROM M_RETAIL
WHERE BILLDATE = 20260501
AND STATUS = '2'
AND ROWNUM <= 20
```
避免示例:
```sql
SELECT * FROM M_RETAIL
```
## 常用业务对象说明
当用户使用商品、款号、条码、SKU、库存等业务词时,默认按以下对象理解:
- “商品”“款号”通常指 `M_PRODUCT` 表。
- “条码”“SKU”通常指 `M_PRODUCT_ALIAS` 表。
- 查询条码/SKU 时,首查 `M_PRODUCT_ALIAS.NO` 字段。
- 款号和条码是一对多关系:一个 `M_PRODUCT` 可以对应多条 `M_PRODUCT_ALIAS`。
- 如果涉及库存查询,通常查询 `V_FA_STORAGE` 视图。
- `V_FA_STORAGE` 数据量很大,必须同时带店仓条件和款号/条码条件,不允许只查整张库存视图。
库存查询过滤规则:
- 店仓条件应优先使用门店、仓库、店仓 ID 或店仓编码等字段,先通过 `describe` 或 `AD_COLUMN` 确认实际字段名。
- 款号条件走 `M_PRODUCT`;条码/SKU 条件走 `M_PRODUCT_ALIAS`。
- 通过条码查库存时,应先用 `M_PRODUCT_ALIAS.NO` 找到对应商品,再关联或过滤 `V_FA_STORAGE`。
- 必须加 `ROWNUM` 或分页限制;如果用户没有提供店仓或款号/条码,先追问,不要直接查询库存视图。
示例思路:
```sql
-- 伪示例:实际字段名需先 describe / AD_COLUMN 确认
SELECT *
FROM V_FA_STORAGE s
WHERE s.C_STORE_ID = :store_id
AND s.M_PRODUCT_ID = :product_id
AND ROWNUM <= 20
```
## 分析报告内容
`analyze` 命令返回完整分析报告,包含:
- **源码**:完整 PL/SQL 代码
- **依赖**:涉及的表、视图、嵌套存储过程/函数
- **表结构**:相关表的列信息(列名、类型、长度、可空)
- **触发器**:相关表上的触发器列表
## 直接 HTTP 调用
不通过脚本,直接调用中转服务 API:
### 分析存储过程
```bash
curl -X POST http://47.97.171.89:6357/api/query \
-H "Content-Type: application/json" \
-d '{
"server_id": "server-001",
"action": "analyze_procedure",
"schema": "BOS",
"name": "M_RETAIL_SUBMIT",
"timeout": 60
}'
```
### 查询表结构
```bash
curl -X POST http://47.97.171.89:6357/api/query \
-H "Content-Type: application/json" \
-d '{
"server_id": "server-001",
"action": "describe_table",
"schema": "bosnds3",
"name": "xcx_so",
"timeout": 30
}'
```
### action 类型
| action | 说明 |
|--------|------|
| `analyze_procedure` | 完整分析 |
| `list_procedures` | 列出存储过程 |
| `get_source` | 源码 |
| `get_dependencies` | 依赖 |
| `get_tables` | 表结构(通过存储过程依赖) |
| `describe_table` | 查询表结构(列、索引、行数、注释) |
| `execute_query` | 执行 SELECT 查询(只读)—— 表空间查询也复用此 action |
| `schema_discovery` | 发现核心业务域 |
| `generate_nl2sql_schema` | 生成 NL2SQL Schema 字典 |
## 数据字典(AD_TABLE / AD_COLUMN / AD_REFBYTABLE)
### 通用查询规则
处理 BOS 业务单据(如零售单、采购单、调拨单、销售单等)时,必须遵循以下查询路径:
**1. 术语 → 表名(AD_TABLE)**
遇到不懂的业务术语(如"零售单提交"、"采购入库"),先查 AD_TABLE:
```sql
SELECT ID, NAME, TABLENAME, DESCRIPTION, PROC_SUBMIT
FROM AD_TABLE
WHERE NAME LIKE '%关键词%' OR DESCRIPTION LIKE '%关键词%'
```
- `NAME`:表名(英文,如 M_RETAIL)
- `DESCRIPTION`:业务说明(中文,如"零售单")
- `PROC_SUBMIT`:该单据的提交存储过程名称(非空即代表该表有提交逻辑)
**2. 字段含义(AD_COLUMN)**
不理解某个字段的业务含义时,查 AD_COLUMN:
```sql
SELECT DBNAME, NAME, DESCRIPTION, COLTYPE
FROM AD_COLUMN
WHERE AD_TABLE_ID = <AD_TABLE.ID>
ORDER BY ORDERNO
```
**3. 主表 → 明细表(AD_REFBYTABLE)**
对于单据类表,其明细表、付款表等子表必须通过 AD_REFBYTABLE 查找,不要靠猜测命名规则:
```sql
SELECT t1.NAME AS MAIN_TABLE,
t2.NAME AS DETAIL_TABLE,
c.DBNAME AS REF_COLUMN,
r.ASSOCTYPE
FROM AD_REFBYTABLE r
LEFT JOIN AD_TABLE t1 ON r.AD_TABLE_ID = t1.ID
LEFT JOIN AD_TABLE t2 ON r.AD_REFBY_TABLE_ID = t2.ID
LEFT JOIN AD_COLUMN c ON r.AD_REFBY_COLUMN_ID = c.ID
WHERE t1.NAME = '<AD_TABLE.NAME>'
```
- `ASSOCTYPE`:"n"=一对多明细,"1"=强关联/嵌入
- 得到明细表名后,再通过 AD_COLUMN 查看各明细表的字段含义
### 核心表说明
| 表 | 说明 |
|------|------|
| `AD_TABLE` | 所有业务表的元数据注册表,每条记录对应一张业务表 |
| `AD_COLUMN` | 所有业务字段的元数据注册表,含字段名(DBNAME)、显示名(NAME)、描述(DESCRIPTION)、类型(COLTYPE)等 |
| `AD_REFBYTABLE` | **主表与明细表的关联关系**,记录主表→明细表的外键引用 |
### AD_REFBYTABLE 关联字段
| 字段 | 说明 |
|------|------|
| `AD_TABLE_ID` | 主表ID → 关联 AD_TABLE.ID |
| `AD_REFBY_TABLE_ID` | 明细表ID → 关联 AD_TABLE.ID |
| `AD_REFBY_COLUMN_ID` | 明细表中的外键列ID → 关联 AD_COLUMN.ID,通过 AD_COLUMN.DBNAME 获取实际列名 |
| `ASSOCTYPE` | 关联类型("1"=强关联/嵌入, "n"=一对多明细) |
### 查询主表→明细表关系的 SQL 模板
```sql
SELECT t1.NAME AS MAIN_TABLE,
t2.NAME AS DETAIL_TABLE,
c.DBNAME AS REF_COLUMN,
r.ASSOCTYPE
FROM AD_REFBYTABLE r
LEFT JOIN AD_TABLE t1 ON r.AD_TABLE_ID = t1.ID
LEFT JOIN AD_TABLE t2 ON r.AD_REFBY_TABLE_ID = t2.ID
LEFT JOIN AD_COLUMN c ON r.AD_REFBY_COLUMN_ID = c.ID
WHERE t1.NAME = 'M_RETAIL'
```
### 示例:M_RETAIL 的明细表
| 主表 | 明细表 | 外键列 | 关联类型 |
|------|--------|--------|----------|
| M_RETAIL | M_RETAILITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAILPAYITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAIL_PRO_ITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAIL_RATEITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAIL_DISITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAIL_VOUCHERSITEM | M_RETAIL_ID | n |
| M_RETAIL | M_RETAIL_BATCHITEM | M_RETAIL_ID | n |
### 查询字段描述的 SQL 模板
```sql
SELECT DBNAME, NAME, DESCRIPTION, COLTYPE
FROM AD_COLUMN
WHERE AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = 'M_RETAILITEM')
ORDER BY ORDERNO
```
### BOS 系统地图分析(AD_TABLE_TEXT 经验)
`BOSNDS3.AD_TABLE_TEXT` 是 BOS 内部的表配置导出脚本生成器。分析该过程可以反推出 BOS 元数据体系:菜单、模块、业务表、字段、主子表、按钮动作、提交过程、权限目录和报表模板主要由数据字典表驱动。
当用户要求分析“BOS 系统菜单 / 模块目录 / 表结构地图 / 某个业务模块结构”时,优先按下面的元数据链路整理,不要只看物理表:
```text
AD_SUBSYSTEM
-> AD_TABLECATEGORY
-> AD_ACCORDION
-> AD_TABLE
-> AD_COLUMN
-> AD_REFBYTABLE
-> AD_ACTION
-> DIRECTORY
-> AD_CXTAB / AD_CXTAB_JPARA / AD_CXTAB_DIMENSION / AD_CXTAB_FACT
```
核心含义:
- `AD_SUBSYSTEM`:子系统。
- `AD_TABLECATEGORY`:表类别 / 模块分类。
- `AD_ACCORDION`:折叠菜单 / 菜单分组。
- `AD_TABLE`:业务表注册表,含 `DESCRIPTION`、`PROC_SUBMIT`、`DIRECTORY_ID`、`AD_TABLECATEGORY_ID`、`AD_ACCORDION_ID` 等。
- `AD_COLUMN`:字段元数据,含字段名、显示名、业务描述、控件类型、引用列、默认值、限定值组等。
- `AD_REFBYTABLE`:主表到明细表、子表的关系。
- `AD_ACTION`:表动作、按钮、URL、存储过程脚本入口。
- `DIRECTORY`:数据权限目录挂载点。
- `AD_CXTAB` 相关表:公开查询 / 报表模板及其参数、维度、指标。
常用系统地图 SQL:
```sql
SELECT ss.NAME AS SUBSYSTEM,
tc.NAME AS TABLE_CATEGORY,
ac.NAME AS ACCORDION,
t.ID AS AD_TABLE_ID,
t.NAME AS TABLE_NAME,
t.DESCRIPTION,
t.PROC_SUBMIT,
d.NAME AS DIRECTORY_NAME
FROM AD_TABLE t
LEFT JOIN AD_TABLECATEGORY tc ON tc.ID = t.AD_TABLECATEGORY_ID
LEFT JOIN AD_SUBSYSTEM ss ON ss.ID = tc.AD_SUBSYSTEM_ID
LEFT JOIN AD_ACCORDION ac ON ac.ID = t.AD_ACCORDION_ID
LEFT JOIN DIRECTORY d ON d.ID = t.DIRECTORY_ID
WHERE t.NAME LIKE '%关键词%' OR t.DESCRIPTION LIKE '%关键词%'
ORDER BY ss.NAME, tc.ORDERNO, ac.ORDERNO, t.ORDERNO, t.NAME
```
分析单个业务表时,至少同时检查:
- `AD_TABLE`:表归属、提交过程、权限目录。
- `AD_COLUMN`:字段业务含义和值域。
- `AD_REFBYTABLE`:明细表和外键列。
- `AD_ACTION`:按钮和动作脚本。
- `ALL_TRIGGERS` / `USER_OBJECTS`:触发器、同名前缀过程或函数。
注意:这套元数据能还原 BOS 业务结构,不等于完整物理数据库模型。复杂过程内部逻辑、动态 SQL、触发器副作用和前端特殊页面仍需要结合源码继续分析。
## 表空间查询
查询 Oracle 表空间使用情况。
### 自动降级策略
脚本按优先级尝试**5 种方式**,无需人工切换:
| 优先级 | 方式 | 数据来源 | 条件 | 输出字段 |
|:---:|------|----------|------|----------|
| 1️⃣ | 直接 DBA 查询 | `DBA_DATA_FILES` + `DBA_FREE_SPACE` | 用户有 `SELECT_CATALOG_ROLE` | 全部 8 个字段 |
| 2️⃣ | DBA 专用函数 | `sys.get_tablespace_usage()`(返回 SYS_REFCURSOR) | DBA 已执行 CREATE FUNCTION + GRANT | 全部 5 个字段(自动解析) |
| 3️⃣ | DBA 授权 VIEW(无前缀) | `HENLO_TS_USAGE` 视图 | DBA 已执行授权 SQL | 全部 8 个字段 |
| 4️⃣ | DBA 授权 VIEW(sys.前缀) | `SYS.HENLO_TS_USAGE` 视图 | DBA 以 SYS 身份创建时自动处理 | 全部 8 个字段 |
| 5️⃣ | 用户视图 | `USER_FREE_SPACE` | 始终可用 | 仅表空间名称 + 剩余MB |
> **经验**:DBA 以 SYS 身份登录创建的 VIEW 会归属到 `SYS` schema,需用 `sys.HENLO_TS_USAGE` 查询。脚本会自动先试无前缀、再试 `sys.` 前缀。
### 给 DBA 的授权 SQL
如果当前用户缺少系统视图权限,脚本会**自动输出**以下 SQL。将其交给 DBA 在 Oracle 中执行一次:
```sql
CREATE OR REPLACE VIEW SYS.HENLO_TS_USAGE AS
SELECT
df.tablespace_name AS "表空间名称",
ROUND(df.total_bytes / 1048576, 1) AS "总大小MB",
ROUND((df.total_bytes - NVL(fs.free_bytes, 0)) / 1048576, 1) AS "已用MB",
ROUND(NVL(fs.free_bytes, 0) / 1048576, 1) AS "剩余MB",
ROUND((df.total_bytes - NVL(fs.free_bytes, 0)) / df.total_bytes * 100, 1) AS "使用率%",
df.autoextensible AS "自动拓展",
ROUND(df.max_bytes / 1048576, 1) AS "最大可拓展MB",
ROUND((df.total_bytes - NVL(fs.free_bytes, 0)) / NULLIF(df.max_bytes, 0) * 100, 1) AS "最大使用率%"
FROM (
SELECT
tablespace_name,
SUM(bytes) AS total_bytes,
SUM(CASE WHEN autoextensible = 'YES' THEN maxbytes ELSE bytes END) AS max_bytes,
MAX(CASE WHEN autoextensible = 'YES' THEN 'YES' ELSE 'NO' END) AS autoextensible
FROM dba_data_files
GROUP BY tablespace_name
) df
LEFT JOIN (
SELECT tablespace_name, SUM(bytes) AS free_bytes
FROM dba_free_space
GROUP BY tablespace_name
) fs ON df.tablespace_name = fs.tablespace_name
ORDER BY 6 DESC;
GRANT SELECT ON SYS.HENLO_TS_USAGE TO bosnds3;
```
> **原理**:Oracle 视图默认使用**定义者权限**(`DEFINER`),以视图创建者的 DBA 身份执行,普通用户仅需视图的 `SELECT` 权限。这比授予 `SELECT_CATALOG_ROLE` 更安全——只暴露这一个视图的数据。
DBA 执行授权后,再次运行 `tablespace` 命令即可自动使用完整查询。
### 输出字段
| 字段 | 说明 |
|------|------|
| 表空间名称 | 表空间名称 |
| 总大小MB | 当前所有数据文件总大小 |
| 已用MB | 已使用的空间 |
| 剩余MB | 剩余可用空间 |
| 使用率% | 已用 / 总大小 × 100% |
| 自动拓展 | YES=自动扩展,NO=不自动扩展 |
| 最大可拓展MB | 开启自动扩展后的最大可达大小 |
| 最大使用率% | 已用 / 最大可拓展 × 100%(参考阈值 5%) |
### 触发语法
用户可以说:
- 「帮我查询下表空间」
- 「查看表空间使用情况」
- 「表空间是否够用」
```bash
python oracle_skill.py tablespace
# 或
python oracle_skill.py tablespaces
```
### 阈值规则
| 使用率 | 状态 | 建议 |
|--------|------|------|
| < 80% | ✅ 正常 | 无需处理 |
| 80~95% | 🔶 关注 | 评估未来增长,制定扩容计划 |
| ≥ 95% | ⚠️ 紧急 | 尽快扩容或清理空间! |
> **智能告警规则**:脚本逐行解析表空间数据,自动判断衡量指标。
> 若表空间**已开启自动扩展**(`自动拓展=YES`),按 **最大使用率%** 判断(已用/最大可拓展);
> 若**未开启**(`NO`),按当前 **使用率%** 判断(已用/当前总大小)。
>
> 这让告警更准确。例如:SYSTEM 使用率 99.4% 但最大使用率仅 3.7%,说明只是当前数据文件写满了,自动扩展后即释放空间,并非真正的存储瓶颈。
### 使用场景
1. **查主表的明细表**:通过 AD_REFBYTABLE 找到主表关联的所有明细表及外键列
2. **查字段含义**:通过 AD_COLUMN 的 DESCRIPTION 字段了解业务含义
3. **构建 JOIN 查询**:根据外键列名自动拼接主表→明细表的 JOIN 条件
4. **理解业务模型**:通过表间关系理解业务单据结构(如零售单→零售明细→支付明细)
### 4. 统计数据场景的过滤条件
在 BOS 系统中,进行统计数据查询时,必须遵循以下业务规则:
**业务单据通用过滤条件**:
- `ISACTIVE = 'Y'`:单据有效(Y=可用,N=已删除/作废)
- `STATUS = '2'`:单据已提交(1=未提交,2=已提交,3=审批中)
**查询模板**:
```sql
-- 查询有效且已提交的业务单据
SELECT * FROM <业务表名>
WHERE ISACTIVE = 'Y' AND STATUS = '2'
-- 其他业务条件...
```
**字段含义**:
| 字段 | 值 | 业务含义 |
|------|-----|----------|
| `ISACTIVE` | 'Y' | 单据有效,可参与统计 |
| `ISACTIVE` | 'N' | 单据已删除/作废,应排除 |
| `STATUS` | '1' | 草稿/未提交,应排除 |
| `STATUS` | '2' | **已提交**,可参与统计 |
| `STATUS` | '3' | 审批中,通常排除(除非统计审批流程) |
**应用场景**:
- 统计销售额、销售量 → 必须用 `ISACTIVE='Y' AND STATUS='2'`
- 查询历史订单 → 必须用 `ISACTIVE='Y' AND STATUS='2'`
- 分析业务趋势 → 必须用 `ISACTIVE='Y' AND STATUS='2'`
**注意事项**:
1. 明细表(如 M_RETAILITEM)通常继承主表状态,但有时也有自己的状态字段
2. 部分表可能使用 `ISSTOP` 代替 `ISACTIVE`,逻辑相反('Y'=停用)
3. 查询前应先通过 AD_COLUMN 确认字段名和值域
## 数据权限系统
### GET_USERSPERMSQL 函数
数据库内置 `get_userspermsql` 函数,用于获取用户的数据权限过滤条件。
**函数签名:**
```sql
FUNCTION get_userspermsql(
p_users_id NUMBER, -- 用户ID (USERS.ID)
p_tableid NUMBER, -- 表ID (AD_TABLE.ID)
p_col VARCHAR2 -- 字段名 (AD_COLUMN.DBNAME),可为NULL
) RETURN CLOB -- 返回权限过滤SQL
```
**返回值:**
- 空字符串:全权限(无需过滤)
- SQL条件:如 `C_STORE_ID IN (...)`,需拼接到 WHERE 子句
**权限模型:**
```
用户 → GROUPUSER → 用户组 → GROUPPERM → 安全目录 → 权限SQL
↓
AD_TABLE.DIRECTORY_ID
```
### 使用权限过滤查询
**1. 查询用户权限:**
```bash
python oracle_skill.py perm 940 12983
```
返回:
```json
{
"user_id": "940",
"table_id": "12983",
"perm_sql": "M_OTHER_INOUT.C_STORE_ID IN(...)",
"has_restriction": true
}
```
**2. 带权限过滤的查询:**
```bash
python oracle_skill.py qperm 940 12983 "SELECT * FROM M_OTHER_INOUT WHERE BILLDATE=20260501"
```
自动拼接权限条件后执行:
```sql
SELECT * FROM M_OTHER_INOUT
WHERE BILLDATE=20260501
AND M_OTHER_INOUT.C_STORE_ID IN(...)
```
### 常用表ID参考
| 表名 | AD_TABLE.ID | 说明 |
|------|-------------|------|
| M_RETAIL | 12964 | 零售单主表 |
| M_RETAILITEM | 12965 | 零售明细 |
| M_OTHER_INOUT | 12983 | 其他出入库 |
| C_STORE_CHKDAY | 18182 | 门店盘点日 |
## 故障排查
| 问题 | 排查 |
|------|------|
| `无法连接中转服务` | 检查 transit_url 地址和端口 |
| `agent not found` | 确认 Agent 已启动并连接成功 |
| `请求超时` | Agent 可能卡住,增大 timeout 参数 |
| Oracle 连接失败 | 检查 Agent 的 config.json 中 Oracle 配置 |
## Oracle 日常运维监控触发
当用户说“给我 HENLO 的 Oracle 运维报告”、“生成 HENLO 数据库日报”、“做一次 HENLO Oracle 巡检”这类请求时,调用:
```bash
python scripts/oracle_skill.py ops_report HENLO
```
当用户询问单项运维状态时,调用 `ops <item> [clientCode]`:
```bash
python scripts/oracle_skill.py ops blocking_locks HENLO
python scripts/oracle_skill.py ops active_slow_sql HENLO
python scripts/oracle_skill.py ops ora_errors HENLO
```
常用自然语言映射:
| 用户意图 | 命令 |
|---|---|
| 有没有锁阻塞 | `ops blocking_locks <clientCode>` |
| 当前有没有慢 SQL | `ops active_slow_sql <clientCode>` |
| 最近 ORA 错误 | `ops ora_errors <clientCode>` |
| UNDO 风险 | `ops undo <clientCode>` |
| IO 等待 | `ops io_waits <clientCode>` |
| 内存使用 | `ops memory <clientCode>` |
| 失效对象 | `ops invalid_objects <clientCode>` |
涉及 SYS/DBA 权限视图时,Agent 会优先调用 `SYS.HENLO_ORA_MONITOR`;如果不可用,再尝试当前登录账号下的 `HENLO_ORA_MONITOR`。如果两个函数都不存在或未授权,输出两种安装方案:有 SYS 账号时安装到 SYS 并授权;没有 SYS 账号但当前登录账号已有系统视图查询权限时,安装到当前账号后重试。
## 服务器体检报告触发
当用户说“给我 HENLO 的服务器体检报告”、“查询 HENLO 的服务器状态”、“看看 HENLO 的备份情况”这类请求时,调用:
```bash
python scripts/oracle_skill.py checkup HENLO
```
如果用户没有指定客户服务器编号,则调用:
```bash
python scripts/oracle_skill.py checkup
```
输出字段固定为 `clientCode`、`info_cpu_rate`、`info_memory_rate`、`info_memory`、`bak_path`、`bak_lastdate`、`computer`。`clientCode` 是客户服务器编号,非必填;不传时使用当前登录 client。
## 新增业务记录 SQL 生成规则
当用户要求分析或生成新增业务表记录的 SQL 示例时,仍然保持只读安全边界:skill 可以生成 SQL 和说明,但不能直接通过查询通道执行 INSERT、UPDATE、DELETE、MERGE、DDL 等写操作。
生成新增记录 SQL 时遵守以下默认规则:
- 主表或子表新增记录的主键 `ID` 使用 `get_sequences('<表名称>')` 取值。
- 如果目标表存在 `DOCNO` 字段,先查询 `AD_COLUMN` 中该字段是否配置 `SEQUENCENAME` 单据编号生成器。
- 如果 `DOCNO` 对应的 `SEQUENCENAME` 有值,则单据编号使用 `Get_SequenceNo('<SEQUENCENAME>', 37)` 取值。
- 如果目标表存在 `AD_CLIENT_ID` 字段,默认值使用 `37`。
- 如果目标表存在 `AD_ORG_ID` 字段,默认值使用 `27`。
- 子表的 `AD_CLIENT_ID` 和 `AD_ORG_ID` 默认取父表记录中对应字段值,不单独写固定值。
- 新增业务表单时,如果目标表存在 `STATUS`、`STATUSERID`、`STATUSTIME` 字段,通常只给 `STATUS` 赋默认值 `'1'`,不对 `STATUSERID`、`STATUSTIME` 赋值。
- `STATUS='1'` 表示草稿/未提交;提交人、提交时间等字段由提交动作或提交存储过程处理。
- 如果用户明确提到“提交”,通常不是直接把 `STATUS` 改成 `'2'`,而是应先查询 `AD_TABLE.PROC_SUBMIT`,按该表配置的提交存储过程理解提交逻辑。
查询 `DOCNO` 编号生成器示例:
```sql
SELECT SEQUENCENAME
FROM AD_COLUMN
WHERE AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = '<表名称>')
AND UPPER(DBNAME) = 'DOCNO'
```
## 字段值域映射规则(AD_COLUMN / AD_LIMITVALUE)
当用户要求解释字段含义、生成查询条件、分析业务类型,或生成新增业务记录 SQL 示例时,如果目标字段在 `AD_COLUMN` 中的 `OBTAINMANNER` 值为 `select`,必须继续通过 `AD_LIMITVALUE_GROUP_ID` 查询 `AD_LIMITVALUE`,拿到显示值与数据库实际值的映射关系。
关键规则:
- `AD_COLUMN.OBTAINMANNER = 'select'` 表示该字段是受限值列表。
- `AD_COLUMN.AD_LIMITVALUE_GROUP_ID` 关联 `AD_LIMITVALUE.AD_LIMITVALUE_GROUP_ID`。
- 用户通常说的是显示值,例如“券核销”。
- SQL 查询条件和新增记录 SQL 必须使用数据库实际值,例如 `VOU_USED`。
- 不要把显示值直接写入业务表字段,除非 `AD_LIMITVALUE` 查询结果证明显示值就是实际值。
示例:`XH_ORDER_FTP.BILLTYPE` 字段,当显示值为“券核销”时,对应的数据库实际值是 `VOU_USED`。因此用户说“类型为券核销”时,应生成:
```sql
XH_ORDER_FTP.BILLTYPE = 'VOU_USED'
```
查询字段值域映射模板:
```sql
SELECT c.DBNAME,
c.NAME,
c.OBTAINMANNER,
c.AD_LIMITVALUE_GROUP_ID,
v.VALUE,
v.NAME AS DISPLAY_NAME
FROM AD_COLUMN c
LEFT JOIN AD_LIMITVALUE v ON v.AD_LIMITVALUE_GROUP_ID = c.AD_LIMITVALUE_GROUP_ID
WHERE c.AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = '<表名称>')
AND UPPER(c.DBNAME) = UPPER('<字段名>')
ORDER BY v.ORDERNO, v.ID
```
使用时先查字段:
```sql
SELECT ID, DBNAME, NAME, DESCRIPTION, OBTAINMANNER, AD_LIMITVALUE_GROUP_ID
FROM AD_COLUMN
WHERE AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = 'XH_ORDER_FTP')
AND UPPER(DBNAME) = 'BILLTYPE'
```
如果 `OBTAINMANNER='select'` 且 `AD_LIMITVALUE_GROUP_ID` 有值,再查:
```sql
SELECT VALUE, NAME AS DISPLAY_NAME
FROM AD_LIMITVALUE
WHERE AD_LIMITVALUE_GROUP_ID = <AD_LIMITVALUE_GROUP_ID>
ORDER BY ORDERNO, ID
```