chore: publish secure public mirror
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
outputs/
|
||||||
|
scripts/config.json
|
||||||
|
scripts/*.token
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# oracle-jump-query Skill Release Standard
|
||||||
|
|
||||||
|
A published `VERSION` is frozen. Increment it for every later change, update `CHANGELOG.md`, commit and push to Gitea. Skill updates use Git only: pull the commit into the Codex Skill directory with `git pull --ff-only origin main`, then run `capabilities --json`. Do not publish or update this Skill through OSS.
|
||||||
|
|
||||||
|
`inspection_report` and `inspection_latest --refresh` must run `sys_function_check` before Agent collection. Missing SYS/current-schema functions stop generation and return the complete SQL bundle. Historical report reads do not run this preflight.
|
||||||
|
|
||||||
|
Record version, commit, push time and verification result for every release.
|
||||||
|
|
||||||
|
Release 1.5.32: added authenticated `awr_status`, `awr_list`, and byte-for-byte `awr_download` commands. AWR remains disabled until the customer DBA confirms Diagnostics Pack authorization and Agent configuration explicitly enables it. The release commit, push time, and installed-skill verification are recorded in the project release log.
|
||||||
|
|
||||||
|
Release 1.5.33: when AWR readiness reports missing function or view permission, resolve the target Agent schema and return the archived DBA SQL with the real account substituted. Never execute the SQL and never emit an unresolved `<AGENT_SCHEMA>` placeholder.
|
||||||
|
|
||||||
|
Release 1.5.34: update archived `HENLO_AWR_EXPORT` to v1.1 so null rows do not reach `DBMS_LOB.WRITEAPPEND`, preserve report row boundaries with newlines, and keep deployment strictly manual through the DBA SQL output.
|
||||||
|
|
||||||
|
Release 1.5.35: change the Skill default transit endpoint to `https://ts.henlo.net`, update configuration and documentation examples, preserve existing login/session fields while migrating only `transit_url`, validate, commit and push to Gitea, then pull and verify the installed Skill.
|
||||||
+382
@@ -0,0 +1,382 @@
|
|||||||
|
## 1.5.35 (2026-08-14)
|
||||||
|
|
||||||
|
### HTTPS transit endpoint
|
||||||
|
- Changed the default Skill transit endpoint from `http://bos.henlo.net:6357` to `https://ts.henlo.net`.
|
||||||
|
- Updated runtime defaults, configuration templates, examples, direct HTTP examples, and capability metadata to use the HTTPS domain.
|
||||||
|
- Existing runtime tokens and selected clients are preserved when only `transit_url` is migrated.
|
||||||
|
|
||||||
|
## 1.5.34 (2026-07-21)
|
||||||
|
|
||||||
|
### AWR export function recovery
|
||||||
|
- Updated archived `SYS.HENLO_AWR_EXPORT` to v1.1: ignore null report rows before `DBMS_LOB.WRITEAPPEND` and preserve row boundaries with an explicit newline.
|
||||||
|
- Fixes production `ORA-06502` raised by the v1.0 function when Oracle's AWR report stream contains an empty row.
|
||||||
|
- The Skill continues to return the SQL for manual DBA execution only and never runs DDL.
|
||||||
|
|
||||||
|
## 1.5.33 (2026-07-21)
|
||||||
|
|
||||||
|
### AWR DBA preparation
|
||||||
|
- When `awr_status` reports `permission_required`, the Skill now resolves the target Agent's actual Oracle schema and returns the archived `HENLO_AWR_EXPORT` deployment/grant SQL for manual DBA execution.
|
||||||
|
- The SQL is never executed by the Skill and is only shown after reminding the operator to confirm Diagnostics Pack authorization.
|
||||||
|
- Added the explicit `DBA_HIST_SNAPSHOT` read grant required by Agent readiness checks; unresolved schemas never produce SQL containing the `<AGENT_SCHEMA>` placeholder.
|
||||||
|
|
||||||
|
## 1.5.32 (2026-07-21)
|
||||||
|
|
||||||
|
### AWR report delivery
|
||||||
|
- Added `awr_status`, `awr_list`, and `awr_download` commands using authenticated transit-server APIs.
|
||||||
|
- AWR HTML is downloaded byte-for-byte from the Agent and is never rendered locally; downloads are limited to 50 MB and use strict `yyyyMMdd` validation.
|
||||||
|
- Documented the mandatory Diagnostics Pack confirmation and Agent `awr.enabled` / `awr.license_confirmed` gates.
|
||||||
|
|
||||||
|
## 1.5.31 (2026-07-21)
|
||||||
|
|
||||||
|
### Client authorization refresh
|
||||||
|
- `clients` now replaces the local client cache with the transit server's current authorization snapshot, so newly authorized clients appear and revoked clients disappear without re-entering `secretKey`.
|
||||||
|
- Client refresh continues to use only the existing transit-server access token.
|
||||||
|
|
||||||
|
## 1.5.29 (2026-07-15)
|
||||||
|
|
||||||
|
### Inspection SYS deployment
|
||||||
|
- When inspection preflight fails, the Skill reads the target Agent's configured Oracle schema through the `version` action.
|
||||||
|
- SYS function SQL replaces `<AGENT_SCHEMA>` with the actual customer schema before it is shown to the user.
|
||||||
|
- If the schema cannot be determined or is invalid, the Skill stops with a configuration error and never emits placeholder authorization SQL.
|
||||||
|
- Updated `HENLO_ORA_MONITOR` to the complete v3.0 CLOB JSON contract used by Agent inspection actions.
|
||||||
|
|
||||||
|
## 1.5.28 (2026-07-13)
|
||||||
|
|
||||||
|
### Inspection preflight
|
||||||
|
- Restored mandatory `sys_function_check` before `inspection_report` creates a new report.
|
||||||
|
- Missing SYS/current-schema monitor functions now stop collection and return the complete deployment and grant SQL bundle.
|
||||||
|
- Reading an existing latest report no longer requires SYS preflight; `inspection_latest --refresh` still does.
|
||||||
|
|
||||||
|
## 1.5.26 (2026-07-13)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Documented the Skill OSS release path and planned `skill_update` check/download/confirm workflow.
|
||||||
|
- Documented preservation of local `config.json`, `outputs/` and logs during updates.
|
||||||
|
- Marked the runtime `skill_update` command as not implemented yet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.25 (2026-07-13)
|
||||||
|
|
||||||
|
### HTML archive delivery
|
||||||
|
- Retry short-lived 404 responses while transit-server asynchronously commits the report archive.
|
||||||
|
- Return a readable error after retries instead of a traceback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.24 (2026-07-13)
|
||||||
|
|
||||||
|
### HTML download
|
||||||
|
- Fixed HTML URL construction when the local config does not contain a duplicate `transit_url` field.
|
||||||
|
- Network download failures now return a readable Skill error instead of a Python traceback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.23 (2026-07-13)
|
||||||
|
|
||||||
|
### Inspection preflight
|
||||||
|
- Fixed the missing `check_inspection_sys_functions` implementation.
|
||||||
|
- Inspection generation now stops before Agent collection when the SYS monitor function is unavailable and returns the complete grant SQL bundle.
|
||||||
|
- Protected HTML URLs must be downloaded through Skill authentication.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.22 (2026-07-13)
|
||||||
|
|
||||||
|
### 发布规范
|
||||||
|
- 完善 Skill 版本号、CHANGELOG、项目 AGENTS.md 发布记录和实际提交的一致性要求。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.21 (2026-07-13)
|
||||||
|
|
||||||
|
### SYS function deployment
|
||||||
|
- Added `sys_functions <schema> [--json]` to generate the complete SYS function deployment bundle for Agent installation.
|
||||||
|
- The command automatically includes every `.sql` file under `references/sys-functions/` and substitutes `<AGENT_SCHEMA>` with the customer Agent schema.
|
||||||
|
- Documented the rule that every future SYS function must be archived in that directory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.20 (2026-07-10)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 文档补充 Agent 版本查询命令:ersion agent <clientCode...>、--all、--json。
|
||||||
|
- 明确在线 Agent 直接确认版本,离线客户返回最后心跳版本和离线状态。
|
||||||
|
- 补充自然语言触发示例:查询所有客户的 Agent 版本号、查询指定客户版本号。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.20 (2026-07-10)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 巡检 HTML/Markdown 支持服务器 IP、操作系统版本、启动时间、运行时长、关键进程和 TCP 连接统计。
|
||||||
|
- 巡检报告支持 Oracle ACTIVE/总会话数、Buffer Cache 与 Library Cache 命中率,并区分真实零值和采集失败。
|
||||||
|
- 修正慢 SQL 列映射、表空间容量/自动扩展上限映射和高内存事件误标为 OOM 的报告问题。
|
||||||
|
- AWR 明确列入未来开发计划,当前版本不采集、不生成 AWR 报告。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.19 (2026-07-10)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 新版服务器巡检命令成功返回后默认生成固定格式 HTML 文件,无需再显式传入 `--html`。
|
||||||
|
- `inspection_report`、`inspection_latest`、`inspection_get` 增加 `--markdown [path]`(别名 `--md`),可在生成 HTML 的同时导出固定格式 Markdown 文件。
|
||||||
|
- `--json` 输出增加 `artifacts` 字段,返回已生成的 HTML/Markdown 文件路径,同时保持标准 JSON 输出。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.18 (2026-07-10)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 移除旧版 `checkup` 对外命令能力,服务器体检统一走新版 `inspection_report` / `inspection_latest` / `inspection_get`。
|
||||||
|
- `inspection_report`、`inspection_latest`、`inspection_get` 增加 `--html [path]`,按固定 HTML 模板导出巡检报告;不传路径时写入 `outputs/`。
|
||||||
|
- `config.json` 读取兼容 UTF-8 BOM。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.17 (2026-07-09)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 增加 `inspection_report [clientCode] [--json]` 命令,通过 transit-server 生成服务器巡检报告并归档。
|
||||||
|
- 增加 `inspection_latest [clientCode] [--refresh] [--json]` 命令,读取最近归档巡检报告;加 `--refresh` 时重新生成后返回。
|
||||||
|
- 增加 `inspection_get <id> [--json]` 命令,按 `TS_INSPECTION_REPORT.ID` 读取历史巡检报告。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.16 (2026-07-08)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 记录 skill Git 源地址:`http://124.220.155.147:3000/qiangayi/oracle-jump-query`。
|
||||||
|
- 明确后续更新 skill 时以该 Git 地址为准,已安装目录只作为运行和验证目录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.15 (2026-07-08)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 补充 BOS 提交流程规则:生成提交 SQL 或说明提交流程时,如果已知 `userId`,调用 `AD_TABLE.PROC_SUBMIT` 前需要先更新主单据修改人和修改时间,再调用提交存储过程。
|
||||||
|
- 强调修改人、修改时间字段名必须先通过 `AD_COLUMN` 或表结构确认,不要凭经验硬写字段名。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.14 (2026-07-08)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 补充 `AD_TABLE` 表单事件过程规则:`HAS_TRIG_BD='Y'` 时 `TRIG_BD` 为删除前触发过程(bd),`HAS_TRIG_AC='Y'` 时 `TRIG_AC` 为新增后触发过程(ac),`HAS_TRIG_AM='Y'` 时 `TRIG_AM` 为修改后触发过程(am)。
|
||||||
|
- 更新 BOS 元数据查询模板,分析业务表时同时查看 `PROC_SUBMIT` 和 `TRIG_BD/TRIG_AC/TRIG_AM`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.13 (2026-07-06)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 在 `SKILL.md`/README/命令说明中补充 `agent_update [clientCode] [timeout]` 能力,便于其他 Agent 通过 `capabilities` 发现并触发客户 Agent 更新。
|
||||||
|
- skill 默认中转机统一为 `http://bos.henlo.net:6357`,不再使用旧 IP 或 `zjhenlo.henlo.net`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.12 (2026-07-06)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- 增加 `agent_update [clientCode] [timeout]` 命令,通过中转机触发 Agent 自动更新;升级包下载地址由中转机返回,按 OSS `latest.json` 方案下载。
|
||||||
|
- 默认中转机地址调整为 `http://bos.henlo.net:6357`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.11 (2026-07-02)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 增加商品明细常见字段说明:`M_PRODUCT_ID` 对应款号,`M_PRODUCTALIAS_ID` 对应条码,`M_ATTRIBUTESETINSTANCE_ID` 对应色码属性 ASI。
|
||||||
|
- 增加 ASI 色码属性规则:`M_ATTRIBUTESETINSTANCE.VALUE1/VALUE1_CODE/VALUE1_ID` 对应颜色,关联 `M_COLOR.ID`;`VALUE2/VALUE2_CODE/VALUE2_ID` 对应尺码,关联 `M_SIZE.ID`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.10 (2026-07-01)
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
- `clients` 和 `status` 成功获取服务端 client 列表后,会同步刷新本地 `client_list` 缓存。
|
||||||
|
- `switch` 支持按 client code 或 client title/name 切换;找不到时自动刷新 client 列表后再匹配。
|
||||||
|
- client 名称模糊匹配到多个结果时,不自动切换,只列出候选项并要求用户指定更准确的 client code。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.9 (2026-07-01)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 整理 `SKILL.md` 为执行入口和索引页,将详细说明拆分到 `references/`。
|
||||||
|
- 新增完整原文备份 `references/legacy-skill-v1.5.8.md`,确保整理过程不遗漏既有内容。
|
||||||
|
- 记录文档维护规则:精简主文档时只能移动内容到 references,删除内容必须先经过用户同意。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.8 (2026-07-01)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 记录 `AD_TABLE_TEXT` 分析结论:可通过 BOS 元数据表反推出系统菜单、模块目录、业务表、字段、主子表、动作、权限目录和报表模板结构。
|
||||||
|
- 增加 BOS 系统地图分析链路:`AD_SUBSYSTEM -> AD_TABLECATEGORY -> AD_ACCORDION -> AD_TABLE -> AD_COLUMN / AD_REFBYTABLE / AD_ACTION`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.7 (2026-06-17)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 增加条码/SKU 查询优先字段:查询条码时首查 `M_PRODUCT_ALIAS.NO`,再根据条码关联商品和库存。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.6 (2026-06-17)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 增加常用业务对象说明:商品/款号默认对应 `M_PRODUCT`,条码/SKU 默认对应 `M_PRODUCT_ALIAS`,款号与条码是一对多关系。
|
||||||
|
- 增加库存查询约束:库存通常查询 `V_FA_STORAGE`,该视图数据量大,必须通过店仓 + 款号/条码条件查询,并配合行数限制。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.5 (2026-06-17)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 增加生产查询限制说明:直接查询业务表时必须带明确过滤条件和行数限制,不要执行无条件整表查询。
|
||||||
|
- 更新 `query` 示例,强调使用日期、状态、单号、门店、主键等业务条件,并配合 `ROWNUM` 或分页限制。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.4 (2026-06-16)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 记录新增业务表单时的提交状态字段默认规则:如果表存在 `STATUS`、`STATUSERID`、`STATUSTIME`,新增草稿表单通常只设置 `STATUS='1'`,不对 `STATUSERID`、`STATUSTIME` 赋值。
|
||||||
|
- 记录“提交”语义:如果用户提到提交,通常指执行 `AD_TABLE.PROC_SUBMIT` 对应的提交存储过程,提交人和提交时间等字段由提交逻辑处理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.3 (2026-06-16)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 记录 `AD_COLUMN.OBTAINMANNER = 'select'` 的字段值域查询规则:通过 `AD_LIMITVALUE_GROUP_ID` 查询子表 `AD_LIMITVALUE`。
|
||||||
|
- 当用户使用显示值描述业务含义时,生成查询或新增记录 SQL 前必须先映射为数据库实际值。例如 `XH_ORDER_FTP.BILLTYPE` 显示值“券核销”对应实际入库值 `VOU_USED`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.2 (2026-06-15)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 记录新增业务表记录时的 SQL 生成规则:主键 `ID` 使用 `get_sequences('<表名称>')`。
|
||||||
|
- 如果表存在 `DOCNO` 字段,先查询 `AD_COLUMN` 中该字段的 `SEQUENCENAME` 单据编号生成器;有值时使用 `Get_SequenceNo('<SEQUENCENAME>', 37)` 生成单据编号。
|
||||||
|
- 如果表存在 `AD_CLIENT_ID` 字段,默认值为 `37`;如果存在 `AD_ORG_ID` 字段,默认值为 `27`。
|
||||||
|
- 子表的 `AD_CLIENT_ID` 和 `AD_ORG_ID` 默认继承父表对应字段值。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 更新日志
|
||||||
|
|
||||||
|
## 1.5.1 (2026-06-15)
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
- Oracle 运维监控函数调用增加当前账号兜底:优先调用 `SYS.HENLO_ORA_MONITOR`,失败后自动尝试 `HENLO_ORA_MONITOR`。
|
||||||
|
- 函数缺失提示增加双安装方案:可安装到 SYS 并授权,也可在当前 Agent 登录账号已有系统视图权限时安装到当前账号。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.5.0 (2026-06-15)
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
- 新增 `ops <item> [clientCode]`,用于单项 Oracle 日常运维监控。
|
||||||
|
- 新增 `ops_report [clientCode]`,用于生成 Oracle 运维日报。
|
||||||
|
- 支持通过中转机 `/api/oracle_ops` 和 `/api/oracle_ops_report` 调用 Agent 受控运维监控 action。
|
||||||
|
- 支持 `SYS.HENLO_ORA_MONITOR` 定义者权限函数方案,缺失时输出 SYS 函数源码和授权 SQL。
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
- Oracle 运维监控不再由 skill 直接拼 SQL,而是由中转机鉴权、Agent 白名单 action、SYS 函数共同控制。
|
||||||
|
- 报告中单项失败不影响整体输出,失败项会在报告末尾汇总。
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 更新 README、SKILL.md、agent.md,记录 Oracle 运维报告、单项命令和 SYS 权限函数方案。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.4.0 (2026-06-11)
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
- 新增低调入口 `checkup [clientCode]`,用于查询客户服务器体检报告。
|
||||||
|
- 服务器体检报告通过中转机 `/api/server_status` 获取,返回 Markdown 表格。
|
||||||
|
- 体检报告字段包括 `clientCode`、`info_cpu_rate`、`info_memory_rate`、`info_memory`、`bak_path`、`bak_lastdate`、`computer`。
|
||||||
|
- 支持用户自然语言询问“服务器体检报告 / 服务器状态 / 备份情况”时调用 `checkup`。
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
- `login <secretKey> [clientCode]` 改为登录中转机,由中转机调用 BOS `ts_login` 并签发 `access_token`。
|
||||||
|
- `clientCode` 为客户服务器编号,改为非必填;不传时默认使用 BOS 返回的第一个可用 client。
|
||||||
|
- 本地配置不再保存 `secretKey`,只保存中转机 token、过期时间、当前 client 和可访问 client 列表。
|
||||||
|
- 同一个 `secretKey` 同时只允许一个设备在线;新设备登录后旧 token 立即失效。
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
- 更新 `README.md` 和 `SKILL.md`,补充 `checkup` 服务器体检报告说明。
|
||||||
|
- 记录 `clientCode` 非必填,以及 token 失效后需要重新 login 的行为。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 1.3.0 (2026-06-10)
|
||||||
|
|
||||||
|
### ✨ 新增
|
||||||
|
- 表空间查询 5 层自动降级策略
|
||||||
|
- 第 1 层:`DBA_DATA_FILES` + `DBA_FREE_SPACE` 直接查询(需 `SELECT_CATALOG_ROLE`)
|
||||||
|
- 第 2 层:`sys.get_tablespace_usage()` 专用函数(返回 `SYS_REFCURSOR`)
|
||||||
|
- 第 3 层:`HENLO_TS_USAGE` 授权 VIEW
|
||||||
|
- 第 4 层:`SYS.HENLO_TS_USAGE` 授权 VIEW(带 schema 前缀)
|
||||||
|
- 第 5 层:`USER_FREE_SPACE` 回退查询(始终可用)
|
||||||
|
- 自动输出 DBA 授权 SQL(无权限时自动打印 CREATE VIEW + GRANT 语句)
|
||||||
|
- 函数 `SYS_REFCURSOR` 返回值的正则解析,支持从 Go 结构体 dump 中提取表格数据
|
||||||
|
- `version` 命令:查看当前版本号
|
||||||
|
|
||||||
|
### 🔧 修复
|
||||||
|
- 视图创建在 `SYS` schema 下时,自动尝试 `sys.` 前缀查询
|
||||||
|
- 交互模式和 CLI 模式显示统一:区分 DBA 查询 / 函数查询 / VIEW 查询 / 降级回退
|
||||||
|
|
||||||
|
### 📝 文档
|
||||||
|
- `SKILL.md`:新增表空间查询文档(5 层降级 + 授权 SQL + schema 前缀说明)
|
||||||
|
- `docs/操作示例.md`:新增完整操作示例(含实际 HENLO 环境输出)
|
||||||
|
- 新增 `CHANGELOG.md`、`VERSION` 文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.2.0 (2026-06-09)
|
||||||
|
|
||||||
|
### ✨ 新增
|
||||||
|
- 表空间查询基础功能
|
||||||
|
- 支持 `DBA_DATA_FILES` + `DBA_FREE_SPACE` 查询
|
||||||
|
- 交互模式支持 `tablespace` / `tablespaces` 命令
|
||||||
|
- CLI 模式支持 `tablespace` / `tablespaces` 命令
|
||||||
|
|
||||||
|
### 🔧 修复
|
||||||
|
- 权限不足时自动降级到 `USER_FREE_SPACE` 查询
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.1.0 (2026-XX-XX)
|
||||||
|
|
||||||
|
### ✨ 新增
|
||||||
|
- 数据字典查询(AD_TABLE / AD_COLUMN / AD_REFBYTABLE)
|
||||||
|
- `get_tables` 命令:通过存储过程依赖发现表结构
|
||||||
|
- `describe_table` 命令:查询表结构(列、索引、行数、注释)
|
||||||
|
- `get_userspermsql` 命令:生成用户权限 SQL
|
||||||
|
- `perm` 命令:查询用户权限
|
||||||
|
- `permq` 命令:带权限过滤的查询
|
||||||
|
- 交互式模式
|
||||||
|
|
||||||
|
### 🔧 修复
|
||||||
|
- 各项命令的异常处理优化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1.0.0 (2026-XX-XX)
|
||||||
|
|
||||||
|
### ✨ 初始版本
|
||||||
|
- Oracle 跳板查询基础架构
|
||||||
|
- 通过中转服务 + Agent 查询远程 Oracle
|
||||||
|
- 支持存储过程源码分析、依赖查询、触发器查询
|
||||||
|
- 多租户授权验证(BOS ts_login / ts_client_check)
|
||||||
|
- 支持 `analyze`、`source`、`list`、`deps`、`trigger` 等命令
|
||||||
|
- 支持 `query` 命令直接执行 SELECT(只读)
|
||||||
|
|
||||||
|
## 1.5.30 (2026-07-21)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Documented `AD_TABLE.MASK` as the table's declared business-action codes: `A` add, `M` modify, `D` delete, `Q` query, `U` cancel submission, `V` void, and `S` submit.
|
||||||
|
- Restored Git/Gitea as the only Skill update source; removed the OSS and `skill_update` release/update instructions.
|
||||||
@@ -0,0 +1,345 @@
|
|||||||
|
# Oracle Jump Query Skill
|
||||||
|
|
||||||
|
通过中转服务查询远程 Oracle 数据库的 AI 技能,支持存储过程分析、表结构查询、数据权限控制。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
用户/AI → 中转服务 (:6357) → WebSocket → Agent → Oracle 数据库
|
||||||
|
```
|
||||||
|
|
||||||
|
- **中转服务**:转发 HTTP 请求到数据库服务器的 Agent
|
||||||
|
- **Agent**:部署在数据库服务器,通过 WebSocket 连接中转服务,无需开放数据库端口
|
||||||
|
- **安全**:数据库服务器仅需出站 WebSocket 连接,防火墙友好
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
- Python 3.6+
|
||||||
|
- `pip install requests`
|
||||||
|
|
||||||
|
## 维护来源
|
||||||
|
|
||||||
|
本 skill 的 Git 源地址:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://124.220.155.147:3000/qiangayi/oracle-jump-query
|
||||||
|
```
|
||||||
|
|
||||||
|
需要更新 skill 时,以该地址为准进行拉取、修改、提交和同步。`C:\Users\qiang\.codex\skills\oracle-jump-query` 是安装后的运行目录,不作为长期源码目录。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
更多面向日常使用的问法和操作流程,见 [操作示例](docs/操作示例.md)。
|
||||||
|
|
||||||
|
### 1. 配置
|
||||||
|
|
||||||
|
编辑 `scripts/config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"transit_url": "https://ts.henlo.net",
|
||||||
|
"server_id": "server-001",
|
||||||
|
"access_token": "",
|
||||||
|
"expires_at": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
先登录中转机后再查询:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python oracle_skill.py login <secretKey> [clientCode]
|
||||||
|
python oracle_skill.py status
|
||||||
|
```
|
||||||
|
|
||||||
|
`secretKey` 只用于登录请求,不会保存到本地配置文件。`clientCode` 是客户服务器编号,非必填;不传时中转机会默认选择 BOS 返回的第一个可用 client。登录成功后本地仅保存中转机签发的 `access_token`、过期时间和可访问 client 列表。
|
||||||
|
|
||||||
|
同一个 `secretKey` 同时只允许一个设备在线。另一台设备重新登录后,当前设备的 token 会立即失效,需要重新执行 `login <secretKey> [clientCode]`。
|
||||||
|
|
||||||
|
### 2. 运行交互模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
python oracle_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 运行指定命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 分析存储过程
|
||||||
|
python oracle_skill.py analyze bosnds3 M_RETAIL_SUBMIT
|
||||||
|
|
||||||
|
# 查询表结构
|
||||||
|
python oracle_skill.py describe bosnds3 xcx_so
|
||||||
|
|
||||||
|
# 执行 SELECT 查询:生产表必须带业务过滤条件和行数限制
|
||||||
|
python oracle_skill.py query "SELECT * FROM M_RETAIL WHERE BILLDATE = 20260501 AND ROWNUM <= 20"
|
||||||
|
|
||||||
|
# 获取用户数据权限
|
||||||
|
python oracle_skill.py perm 940 12983
|
||||||
|
```
|
||||||
|
|
||||||
|
## 命令列表
|
||||||
|
|
||||||
|
| 命令 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `analyze <schema> <proc>` | 完整分析存储过程(源码+依赖+表+触发器) | `analyze bosnds3 M_RETAIL_SUBMIT` |
|
||||||
|
| `list <schema>` | 列出 schema 下所有存储过程 | `list bosnds3` |
|
||||||
|
| `source <schema> <proc>` | 获取存储过程源码 | `source bosnds3 M_RETAIL_SUBMIT` |
|
||||||
|
| `deps <schema> <proc>` | 获取依赖(表/存储过程) | `deps bosnds3 M_RETAIL_SUBMIT` |
|
||||||
|
| `describe <schema> <table>` | 查询表结构(列、类型、索引、行数) | `describe bosnds3 xcx_so` |
|
||||||
|
| `query <SQL>` | 执行 SELECT 查询(仅支持 SELECT;生产表必须带过滤条件和行数限制) | `query SELECT * FROM M_RETAIL WHERE BILLDATE=20260501 AND ROWNUM<=20` |
|
||||||
|
| `tablespace / tablespaces` | 查询表空间使用情况 | `tablespace` |
|
||||||
|
| `perm <userId> <tableId>` | 查询用户数据权限 | `perm 940 12983` |
|
||||||
|
| `qperm <userId> <tableId> <SQL>` | 带权限过滤的查询 | `qperm 940 12983 SELECT * FROM M_OTHER_INOUT` |
|
||||||
|
| `login <secretKey> [clientCode]` | 登录中转机 + 选择客户服务器,clientCode 非必填 | `login mykey HENLO` |
|
||||||
|
| `logout` | 登出中转机并清除本地 token | `logout` |
|
||||||
|
| `status` | 查看中转机登录状态 + 当前 client | `status` |
|
||||||
|
| `switch <clientCode|clientName>` | 按 code 或名称切换 client;找不到时自动刷新 client 列表 | `switch HENLO` |
|
||||||
|
| `clients` | 无感刷新当前用户最新授权的 client 列表及在线状态(只使用当前登录 token) | `clients` |
|
||||||
|
| `inspection_report [clientCode] [--json] [--html [path]] [--markdown [path]]` | 生成服务器巡检报告并归档,默认导出固定格式 HTML,可同时导出 Markdown | `inspection_report HENLO --markdown` |
|
||||||
|
| `inspection_latest [clientCode] [--refresh] [--json] [--html [path]] [--markdown [path]]` | 查看最近归档巡检报告并默认导出 HTML;加 `--refresh` 时重新生成 | `inspection_latest HENLO --refresh --markdown` |
|
||||||
|
| `inspection_get <id> [--json] [--html [path]] [--markdown [path]]` | 按归档 ID 查看历史巡检报告并默认导出 HTML | `inspection_get 123 --markdown` |
|
||||||
|
| `agent_update [clientCode] [timeout]` | 触发客户 Agent 自动更新,升级包由 Agent 从 OSS 下载 | `agent_update HENLO 300` |
|
||||||
|
| `discover [schema] [filter]` | 发现核心业务表 | `discover bosnds3 M_%` |
|
||||||
|
| `nl2sql <schema> <question>` | 自然语言转 SQL | `nl2sql bosnds3 查询花都二店5月销售额` |
|
||||||
|
| `servers` | 列出在线 Agent | `servers` |
|
||||||
|
| `health` | 检查中转服务状态 | `health` |
|
||||||
|
| `capabilities --json` | 获取版本和命令定义(供程序调用) | `capabilities --json` |
|
||||||
|
|
||||||
|
## 生产查询限制
|
||||||
|
|
||||||
|
查询业务表时一定要带条件查询,不要直接查询整表。生产环境中业务表通常数据量很大,无条件 `SELECT * FROM <table>` 可能返回大量结果、拖慢 Agent 或影响数据库。
|
||||||
|
|
||||||
|
建议:
|
||||||
|
|
||||||
|
- 必须包含 `WHERE` 条件,优先使用日期、单号、门店、客户、状态、主键等业务过滤条件。
|
||||||
|
- 必须加 `ROWNUM <= N` 或分页限制。
|
||||||
|
- 用户没有提供条件时,先追问日期、单号、门店等范围;不要直接查整表。
|
||||||
|
- 只想了解字段时,优先使用 `describe`、`AD_TABLE`、`AD_COLUMN`。
|
||||||
|
|
||||||
|
推荐:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT *
|
||||||
|
FROM M_RETAIL
|
||||||
|
WHERE BILLDATE = 20260501
|
||||||
|
AND STATUS = '2'
|
||||||
|
AND ROWNUM <= 20
|
||||||
|
```
|
||||||
|
|
||||||
|
不要这样查:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM M_RETAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常用业务对象说明
|
||||||
|
|
||||||
|
- “商品”“款号”通常指 `M_PRODUCT` 表。
|
||||||
|
- “条码”“SKU”通常指 `M_PRODUCT_ALIAS` 表。
|
||||||
|
- 查询条码/SKU 时,首查 `M_PRODUCT_ALIAS.NO` 字段。
|
||||||
|
- 款号和条码是一对多关系:一个款号可以对应多个条码/SKU。
|
||||||
|
- 商品明细里常见的 `M_PRODUCT_ID`、`M_PRODUCTALIAS_ID`、`M_ATTRIBUTESETINSTANCE_ID` 分别对应款号、条码、色码属性 ASI。
|
||||||
|
- `M_ATTRIBUTESETINSTANCE` 的 `VALUE1`、`VALUE1_CODE`、`VALUE1_ID` 对应颜色名称、颜色编号、颜色表 `M_COLOR.ID`;`VALUE2`、`VALUE2_CODE`、`VALUE2_ID` 对应尺码名称、尺码编号、尺码表 `M_SIZE.ID`。
|
||||||
|
- 涉及库存查询时,通常查询 `V_FA_STORAGE` 视图。
|
||||||
|
- `V_FA_STORAGE` 数据量很大,必须通过店仓 + 款号/条码条件查询,不要直接查整张库存视图。
|
||||||
|
|
||||||
|
库存查询建议:
|
||||||
|
|
||||||
|
- 用户只说“查库存”但没有给出店仓或款号/条码时,先追问条件。
|
||||||
|
- 通过款号查库存时,围绕 `M_PRODUCT` 找商品。
|
||||||
|
- 通过条码/SKU 查库存时,优先用 `M_PRODUCT_ALIAS.NO` 找条码,再关联到商品。
|
||||||
|
- 通过颜色/尺码查商品明细或库存时,优先用 `M_ATTRIBUTESETINSTANCE` 的 `VALUE1_ID` / `VALUE2_ID` 或 `VALUE1_CODE` / `VALUE2_CODE` 过滤。
|
||||||
|
- 查询 `V_FA_STORAGE` 时必须加店仓条件、款号/条码条件和 `ROWNUM` 或分页限制。
|
||||||
|
|
||||||
|
## 应用场景
|
||||||
|
|
||||||
|
### 场景一:分析存储过程业务逻辑
|
||||||
|
|
||||||
|
分析 `M_RETAIL_SUBMIT` 存储过程:
|
||||||
|
|
||||||
|
```
|
||||||
|
输入:python oracle_skill.py analyze bosnds3 M_RETAIL_SUBMIT
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 完整源码(PL/SQL)
|
||||||
|
- 依赖的表、视图、嵌套存储过程
|
||||||
|
- 涉及的表结构(列名、类型、长度)
|
||||||
|
- 触发器列表
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景二:查询数据字典
|
||||||
|
|
||||||
|
通过 `AD_TABLE`、`AD_COLUMN` 查询表和字段的业务含义:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 查询 M_RETAIL 的字段描述
|
||||||
|
SELECT DBNAME, NAME, DESCRIPTION, COLTYPE
|
||||||
|
FROM AD_COLUMN
|
||||||
|
WHERE AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = 'M_RETAIL')
|
||||||
|
ORDER BY ORDERNO
|
||||||
|
```
|
||||||
|
|
||||||
|
也可以通过 BOS 元数据体系分析系统菜单、模块目录和业务表结构。`AD_TABLE_TEXT` 过程显示 BOS 的系统地图主要由 `AD_SUBSYSTEM -> AD_TABLECATEGORY -> AD_ACCORDION -> AD_TABLE -> AD_COLUMN / AD_REFBYTABLE / AD_ACTION` 驱动,报表模板由 `AD_CXTAB` 相关表维护,权限挂载点在 `DIRECTORY`。
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景三:带权限过滤的查询
|
||||||
|
|
||||||
|
普通用户查询时自动拼接权限条件(如按门店过滤):
|
||||||
|
|
||||||
|
```
|
||||||
|
输入:python oracle_skill.py qperm 940 12983 SELECT * FROM M_OTHER_INOUT
|
||||||
|
|
||||||
|
自动拼接:C_STORE_ID IN(...) WHERE 条件
|
||||||
|
输出:仅返回用户有权限查看的数据
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据权限模型
|
||||||
|
|
||||||
|
数据库内置 `get_userspermsql` 函数:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
FUNCTION get_userspermsql(
|
||||||
|
p_users_id NUMBER, -- 用户ID
|
||||||
|
p_tableid NUMBER, -- 表ID (AD_TABLE.ID)
|
||||||
|
p_col VARCHAR2 -- 字段名,可为NULL
|
||||||
|
) RETURN CLOB -- 返回权限过滤SQL
|
||||||
|
```
|
||||||
|
|
||||||
|
返回值示例:
|
||||||
|
- 空字符串:全权限(无需过滤)
|
||||||
|
- `C_STORE_ID IN(1,2,3)`:仅可查看指定门店
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 问题 | 解决方法 |
|
||||||
|
|------|----------|
|
||||||
|
| 无法连接中转服务 | 检查 `config.json` 中的 `transit_url` 地址和端口 |
|
||||||
|
| agent not found | 确认 Agent 已启动并连接成功 |
|
||||||
|
| 请求超时 | Agent 可能卡住,增大 `timeout` 参数 |
|
||||||
|
|
||||||
|
## 相关项目
|
||||||
|
|
||||||
|
- [恒诺云打印 Print_Tool](https://gitee.com/qiangayi/henlo_mid) - .NET 10 升级项目
|
||||||
|
- [Henlo_Mid 通用接口](https://gitee.com/qiangayi/henlo_mid) - 企业内部系统集成
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
## 服务器巡检报告
|
||||||
|
|
||||||
|
当用户询问“给我 HENLO 的服务器体检报告 / 巡检报告 / 运维体检报告 / 备份和资源情况”时,统一调用新版巡检入口:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO --markdown
|
||||||
|
python scripts/oracle_skill.py inspection_latest HENLO --refresh --markdown
|
||||||
|
```
|
||||||
|
|
||||||
|
巡检命令成功后默认把固定格式 HTML 写入 `outputs/`。增加 `--markdown`(或 `--md`)会同时生成 Markdown;`--html` 和 `--markdown` 后均可指定自定义输出路径。
|
||||||
|
|
||||||
|
HTML 导出使用固定模板,CSS、章节顺序和表格结构保持不变,只替换报告编号、采集时间、指标值、告警和明细行。不传路径时写入 skill 目录下的 `outputs/`。
|
||||||
|
|
||||||
|
当前报告支持服务器 IP、系统版本、启动时间/运行时长、关键进程、TCP 连接统计,以及 Oracle ACTIVE/总会话数和 Buffer/Library Cache 命中率。AWR 已记录在未来开发计划中,当前版本不执行 AWR 采集;详细边界见 `references/oracle-ops-and-checkup.md`。
|
||||||
|
## Oracle 日常运维监控
|
||||||
|
|
||||||
|
单项监控:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py ops active_slow_sql HENLO
|
||||||
|
python scripts/oracle_skill.py ops blocking_locks HENLO
|
||||||
|
python scripts/oracle_skill.py ops ora_errors HENLO
|
||||||
|
```
|
||||||
|
|
||||||
|
统一日报:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py ops_report HENLO
|
||||||
|
```
|
||||||
|
|
||||||
|
`clientCode` 可省略,不传时使用当前登录 client。第一版支持的监控项包括:`active_slow_sql`、`history_top_sql`、`fullscan_sql`、`plan_heavy_sql`、`blocking_locks`、`long_transactions`、`inactive_sessions`、`datafiles`、`undo`、`memory`、`io_waits`、`background_process`、`ora_errors`、`invalid_objects`、`tablespace`。
|
||||||
|
|
||||||
|
涉及 `v$`、`dba_`、AWR、alert log 等 SYS/DBA 权限视图时,Agent 会优先调用 `SYS.HENLO_ORA_MONITOR`,如果不可用则自动尝试当前登录账号下的 `HENLO_ORA_MONITOR`。如果两个函数都不存在或未授权,命令会输出两种安装方案:有 SYS 账号时安装到 SYS 并授权;没有 SYS 账号但当前登录账号已有系统视图查询权限时,直接安装到当前账号。
|
||||||
|
|
||||||
|
生成巡检报告时,Skill 会先执行 SYS 函数版本与授权预检。预检失败后会读取目标 Agent 的实际 Oracle schema,并把完整 SQL 中的授权账号自动替换为该 schema;输出中不得保留 `<AGENT_SCHEMA>` 占位符。无法取得 schema 时停止生成报告并提示检查 Agent 配置。
|
||||||
|
|
||||||
|
## 新增业务记录 SQL 生成规则
|
||||||
|
|
||||||
|
本 skill 默认只读,不直接执行写库操作;当需要分析或生成新增业务记录 SQL 示例时,遵守以下 BOS 默认取值规则:
|
||||||
|
|
||||||
|
- 新增记录主键 `ID` 使用 `get_sequences('<表名称>')`。
|
||||||
|
- 如果表存在 `DOCNO` 字段,先到 `AD_COLUMN` 查询该字段的 `SEQUENCENAME`。
|
||||||
|
- 如果 `SEQUENCENAME` 有值,`DOCNO` 使用 `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'` 表示草稿/未提交;提交相关字段通常由提交逻辑维护。
|
||||||
|
- 如果用户提到“提交”,通常指该表在 `AD_TABLE.PROC_SUBMIT` 中配置的提交存储过程。
|
||||||
|
- 生成提交 SQL 或说明提交流程时,如果已知 `userId`,调用 `AD_TABLE.PROC_SUBMIT` 前需要先更新主单据修改人和修改时间,再调用提交存储过程;修改人、修改时间字段名必须先通过 `AD_COLUMN` 或表结构确认,不要凭经验硬写字段名。
|
||||||
|
- 分析 BOS 业务表时,同时查看 `AD_TABLE` 的表单事件过程:`HAS_TRIG_BD='Y'` 时 `TRIG_BD` 为删除前触发过程(bd),`HAS_TRIG_AC='Y'` 时 `TRIG_AC` 为新增后触发过程(ac),`HAS_TRIG_AM='Y'` 时 `TRIG_AM` 为修改后触发过程(am)。
|
||||||
|
|
||||||
|
## 字段值域映射规则
|
||||||
|
|
||||||
|
当 `AD_COLUMN` 中某个字段的 `OBTAINMANNER` 值为 `select` 时,该字段的可选值不应直接猜测,需要通过 `AD_LIMITVALUE_GROUP_ID` 查询子表 `AD_LIMITVALUE`,取得“数据库实际值”和“显示值”的映射关系。
|
||||||
|
|
||||||
|
示例:`XH_ORDER_FTP.BILLTYPE` 的显示值“券核销”,对应实际入库值是 `VOU_USED`。当用户说“类型为券核销”时,生成查询条件或新增记录 SQL 应使用实际值:
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Agent 版本查询
|
||||||
|
|
||||||
|
自然语言提示词:查询所有客户的 Agent 版本号;或查询 HENLO、RENBEN 的 Agent 版本号。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py version agent HENLO RENBEN --json
|
||||||
|
python scripts/oracle_skill.py version agent --all --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 从 Git 更新 Skill
|
||||||
|
|
||||||
|
Skill 仅通过 Gitea Git 仓库更新,不使用 OSS 或 `skill_update` 命令。源码仓库完成修改、提交和推送后,在已安装目录执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\qiang\.codex\skills\oracle-jump-query
|
||||||
|
git pull --ff-only origin main
|
||||||
|
python scripts/oracle_skill.py capabilities --json
|
||||||
|
```
|
||||||
|
|
||||||
|
在线 Agent 直接确认当前版本;离线客户返回最后一次心跳版本和离线状态。
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
---
|
||||||
|
name: oracle-jump-query
|
||||||
|
description: Oracle 跳板查询技能。通过中转服务查询远程 Oracle 数据库的元数据、存储过程源码、依赖、表结构、触发器、业务字典、表空间、服务器体检和 Oracle 运维监控。连接数据库默认为只读,禁止通过 skill 查询通道直接执行 INSERT、UPDATE、DELETE、MERGE、DDL 等写操作。当用户需要分析 Oracle 存储过程、查找依赖表、查询表结构、理解 BOS 业务元数据、生成只读查询 SQL 或生成需人工确认的写库 SQL 示例时触发。
|
||||||
|
---
|
||||||
|
|
||||||
|
# Oracle 跳板查询
|
||||||
|
|
||||||
|
> **版本:v1.5.35** · [更新日志](./CHANGELOG.md)
|
||||||
|
|
||||||
|
## 使用原则
|
||||||
|
|
||||||
|
使用本 skill 时,先确认当前登录状态和 client。用户指定“恒诺 / 品小二 / 未芮 / 康奈”等服务器时,可以用 `switch <clientCode|clientName>` 按 code 或名称切换;找不到目标服务器时,skill 会先刷新 client 列表再重试匹配。若名称不够准确并匹配到多个 client,只列出候选项,请用户指定更准确的 client code,不要直接猜测切换。
|
||||||
|
|
||||||
|
本 skill 的数据库通道默认只读。可以生成 SQL 和说明,但不得通过 `query` / `qperm` 执行写库语句,包括 `INSERT`、`UPDATE`、`DELETE`、`MERGE`、`DDL`、提交存储过程和其他会改变业务数据的调用。用户要求写库时,只生成 SQL,并明确说明未执行。
|
||||||
|
|
||||||
|
生产环境查询必须带明确 `WHERE` 条件和行数限制。不要为了“先看看数据”查询整表;涉及库存视图、业务大表、统计查询时尤其要先限定门店、日期、单号、状态、款号、条码或主键范围。
|
||||||
|
|
||||||
|
## 参考文档
|
||||||
|
|
||||||
|
详细规则已经拆分到 `references/`,处理对应任务时必须读取相关文件:
|
||||||
|
|
||||||
|
- [命令、登录与调用方式](./references/commands-and-auth.md)
|
||||||
|
登录、client 切换、CLI 命令、HTTP action、故障排查。
|
||||||
|
- [生产查询限制与常用业务规则](./references/query-and-business-rules.md)
|
||||||
|
生产查询限制、商品/款号/条码/库存规则、新增业务记录 SQL 生成规则、字段值域映射。
|
||||||
|
- [BOS 数据字典与系统地图](./references/bos-metadata.md)
|
||||||
|
`AD_TABLE`、`AD_COLUMN`、`AD_REFBYTABLE`、`AD_TABLE_TEXT`、BOS 系统菜单和表结构地图、数据权限模型。
|
||||||
|
- [表空间、服务器体检与 Oracle 运维监控](./references/oracle-ops-and-checkup.md)
|
||||||
|
表空间、服务器体检、Oracle 运维报告、SYS/当前账号函数授权方案和 AWR 未来开发计划。
|
||||||
|
- [整理前完整 SKILL.md 备份](./references/legacy-skill-v1.5.8.md)
|
||||||
|
为防止内容遗漏保留的完整原文;如发现拆分文档缺项,以此为准补回。
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
命令均在 skill 目录执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\qiang\.codex\skills\oracle-jump-query
|
||||||
|
python scripts/oracle_skill.py <command> [args]
|
||||||
|
```
|
||||||
|
|
||||||
|
登录中转机:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py login <secretKey> [clientCode]
|
||||||
|
```
|
||||||
|
|
||||||
|
`clientCode` 是客户服务器编号,非必填;不传时中转机会默认选择 BOS 返回的第一个可用 client。登录成功后本地只保存中转机签发的 `access_token` 和过期时间,不保存 BOS `secretKey`。同一个 `secretKey` 同时只允许一个设备在线,其他设备重新登录后,当前 token 会失效。
|
||||||
|
|
||||||
|
常用命令:
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `status` | 查看登录状态、当前 client 和 Agent 状态 |
|
||||||
|
| `clients` | 无感刷新当前用户最新授权的 client 列表(只使用当前登录 token) |
|
||||||
|
| `switch <clientCode|clientName>` | 按 code 或名称切换当前 client;找不到时自动刷新 client 列表 |
|
||||||
|
| `analyze <schema> <proc>` | 完整分析存储过程 |
|
||||||
|
| `source <schema> <proc>` | 获取存储过程源码 |
|
||||||
|
| `deps <schema> <proc>` | 获取依赖对象 |
|
||||||
|
| `tables <schema> <proc>` | 获取相关表结构 |
|
||||||
|
| `describe <schema> <table>` | 查询表结构 |
|
||||||
|
| `query <SQL>` | 执行只读 SELECT 查询 |
|
||||||
|
| `qperm <userId> <tableId> <SQL>` | 带权限过滤的只读查询 |
|
||||||
|
| `tablespace` / `tablespaces` | 查询表空间 |
|
||||||
|
| `ops <item> [clientCode]` | 单项 Oracle 运维监控 |
|
||||||
|
| `ops_report [clientCode]` | Oracle 运维报告 |
|
||||||
|
| `inspection_report [clientCode] [--json] [--html [path]] [--markdown [path]]` | 生成服务器巡检报告并归档,默认导出固定格式 HTML,可同时导出 Markdown |
|
||||||
|
| `inspection_latest [clientCode] [--refresh] [--json] [--html [path]] [--markdown [path]]` | 查看最近归档巡检报告并默认导出 HTML;加 `--refresh` 时重新生成 |
|
||||||
|
| `inspection_get <id> [--json] [--html [path]] [--markdown [path]]` | 按归档 ID 查看历史巡检报告并默认导出 HTML |
|
||||||
|
| `awr_status [clientCode] [--json]` | 查看 AWR 授权确认、权限、快照和最近生成状态,不触发生成 |
|
||||||
|
| `awr_list [clientCode] [--json]` | 列出 Agent 已生成并保留的 AWR 报告 |
|
||||||
|
| `awr_download <clientCode> <yyyyMMdd> [--output path]` | 经中转机下载 Agent 原始 AWR HTML,不在本地渲染 |
|
||||||
|
| `agent_update [clientCode] [timeout]` | 触发客户 Agent 自动更新;由中转机下发更新动作,Agent 按 OSS latest.json 返回的包地址下载升级 |
|
||||||
|
|
||||||
|
## 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_TABLE` 找表名、业务说明、`MASK`、`PROC_SUBMIT`、权限目录。`MASK` 是该表声明支持的业务动作代码:`A` 新增、`M` 修改、`D` 删除、`Q` 查询、`U` 取消提交、`V` 作废、`S` 提交;可由多个字母组合,分析功能时以实际值为准。
|
||||||
|
- 同查 `AD_TABLE` 的表单事件过程:`HAS_TRIG_BD='Y'` 时 `TRIG_BD` 是删除前触发的存储过程(简称 bd);`HAS_TRIG_AC='Y'` 时 `TRIG_AC` 是新增后触发的存储过程(简称 ac);`HAS_TRIG_AM='Y'` 时 `TRIG_AM` 是修改后触发的存储过程(简称 am)。
|
||||||
|
- 再查 `AD_COLUMN` 理解字段含义、控件类型、默认值、引用列和值域。
|
||||||
|
- 明细表必须通过 `AD_REFBYTABLE` 查找,不要靠命名规则猜。
|
||||||
|
- 若 `AD_COLUMN.OBTAINMANNER='select'`,必须通过 `AD_LIMITVALUE_GROUP_ID` 查询 `AD_LIMITVALUE`,把显示值映射成数据库实际值。
|
||||||
|
- 如果用户提到“提交”,通常指执行或分析 `AD_TABLE.PROC_SUBMIT` 对应提交过程,不要简单理解为直接更新 `STATUS='2'`。
|
||||||
|
- `AD_TABLE_TEXT` 可作为 BOS 系统地图经验来源:它说明菜单、模块、业务表、字段、主子表、动作、权限目录和报表模板主要由 BOS 元数据表驱动。
|
||||||
|
|
||||||
|
常用系统地图 SQL 见 [BOS 数据字典与系统地图](./references/bos-metadata.md)。
|
||||||
|
|
||||||
|
## 业务规则速记
|
||||||
|
|
||||||
|
- 商品、款号通常指 `M_PRODUCT`。
|
||||||
|
- 条码、SKU 通常指 `M_PRODUCT_ALIAS`;查询条码时首查 `M_PRODUCT_ALIAS.NO`。
|
||||||
|
- 款号和条码是一对多关系。
|
||||||
|
- 商品明细里的 `M_PRODUCT_ID`、`M_PRODUCTALIAS_ID`、`M_ATTRIBUTESETINSTANCE_ID` 通常分别对应款号、条码、色码属性 ASI。
|
||||||
|
- `M_ATTRIBUTESETINSTANCE` 是商品色码属性表:`VALUE1`/`VALUE1_CODE`/`VALUE1_ID` 对应颜色名称、颜色编号、颜色表 `M_COLOR.ID`;`VALUE2`/`VALUE2_CODE`/`VALUE2_ID` 对应尺码名称、尺码编号、尺码表 `M_SIZE.ID`。
|
||||||
|
- 库存通常查 `V_FA_STORAGE`,必须同时带店仓条件和款号/条码条件,并加行数限制。
|
||||||
|
- 新增业务记录 SQL 示例中,主键 `ID` 使用 `get_sequences('<表名称>')`。
|
||||||
|
- 如表存在 `DOCNO`,先查 `AD_COLUMN.SEQUENCENAME`;有值时用 `Get_SequenceNo('<SEQUENCENAME>', 37)` 取单据编号。
|
||||||
|
- 如表存在 `AD_CLIENT_ID`,默认值 `37`;如存在 `AD_ORG_ID`,默认值 `27`。
|
||||||
|
- 子表的 `AD_CLIENT_ID`、`AD_ORG_ID` 默认继承父表。
|
||||||
|
- 新增表单若存在 `STATUS`、`STATUSERID`、`STATUSTIME`,通常只设置 `STATUS='1'`,不设置提交人和提交时间。
|
||||||
|
- 生成提交 SQL 或说明提交流程时,如果已知 `userId`,调用 `AD_TABLE.PROC_SUBMIT` 前需要先更新主单据修改人和修改时间,再调用提交存储过程;修改人、修改时间字段名必须先通过 `AD_COLUMN` 或表结构确认,不要凭经验硬写字段名。
|
||||||
|
|
||||||
|
## 运维能力速记
|
||||||
|
|
||||||
|
当用户说“给我 HENLO 的 Oracle 运维报告 / 数据库日报 / Oracle 巡检”时,使用:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py ops_report HENLO
|
||||||
|
```
|
||||||
|
|
||||||
|
当用户说“给我 HENLO 的服务器巡检报告 / 运维体检报告 / 查看最近一次巡检报告”时,使用:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO
|
||||||
|
python scripts/oracle_skill.py inspection_latest HENLO
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO --markdown
|
||||||
|
```
|
||||||
|
|
||||||
|
巡检命令成功后默认在 `outputs/` 生成 HTML;需要 Markdown 时增加 `--markdown` 或 `--md`,也可在参数后指定输出路径。
|
||||||
|
|
||||||
|
生成新报告前必须先执行 `sys_function_check`。若 `SYS.HENLO_ORA_MONITOR` 和当前 schema 的 `HENLO_ORA_MONITOR` 均不可执行或版本契约不兼容,立即停止采集并返回完整 SYS 函数与授权 SQL;不得先生成部分报告。Skill 必须使用目标 Agent 返回的有效 Oracle schema 自动替换 `<AGENT_SCHEMA>`,直接向用户提供可执行 SQL,不得让用户手工修改占位符;无法确定 schema 时停止并提示检查 Agent 配置。读取既有历史报告不需要重复执行该预检,只有 `inspection_latest --refresh` 需要。
|
||||||
|
|
||||||
|
当用户要求“更新客户 Agent / 触发 Agent 升级 / 给某客户发版升级”时,使用:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py agent_update HENLO 300
|
||||||
|
```
|
||||||
|
|
||||||
|
`clientCode` 可省略,默认使用当前已切换的 client;`timeout` 默认 300 秒。该命令只触发中转机的 `/api/admin/agent_update/trigger`,实际升级包由 Agent 从 OSS 下载,不再从 transit-server 本机下载。
|
||||||
|
|
||||||
|
涉及 `v$`、`dba_`、AWR、SYS 权限视图时,Agent 会优先调用 `SYS.HENLO_ORA_MONITOR`;如果不可用,再尝试当前登录账号下的 `HENLO_ORA_MONITOR`。函数缺失或无权限时,输出安装 SQL,让用户用 SYS 或有视图查询权限的当前账号安装后再试。
|
||||||
|
|
||||||
|
AWR 报告只有在客户 DBA 已确认 Diagnostics Pack 授权,且 Agent 配置 `awr.enabled=true`、`awr.license_confirmed=true` 后才可用。日常巡检不会隐式生成 AWR;Skill 只列出或下载 Agent 已生成的 HTML:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py awr_status WEIRUI
|
||||||
|
python scripts/oracle_skill.py awr_list WEIRUI
|
||||||
|
python scripts/oracle_skill.py awr_download WEIRUI 20260720
|
||||||
|
```
|
||||||
|
|
||||||
|
## 内容维护
|
||||||
|
|
||||||
|
本 skill 的 Git 源地址:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://124.220.155.147:3000/qiangayi/oracle-jump-query
|
||||||
|
```
|
||||||
|
|
||||||
|
需要更新 skill 时,以该地址为准进行拉取、修改、提交和同步。不要把 `C:\Users\qiang\.codex\skills\oracle-jump-query` 当作长期源码目录;该目录只用于安装后的运行和验证。
|
||||||
|
|
||||||
|
整理文档时不得删除既有内容。需要精简主 `SKILL.md` 时,只能把内容移动到 `references/`,并更新索引;确实要删除内容,必须先得到用户明确同意。
|
||||||
|
|
||||||
|
|
||||||
|
## Agent version query
|
||||||
|
|
||||||
|
Natural language: query all customer Agent versions, or query HENLO and RENBEN Agent versions.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
- python scripts/oracle_skill.py version agent HENLO RENBEN --json
|
||||||
|
- python scripts/oracle_skill.py version agent --all --json
|
||||||
|
|
||||||
|
Online Agents return their current version; offline customers return the last heartbeat version and offline status.
|
||||||
|
|
||||||
|
## SYS function deployment bundle
|
||||||
|
|
||||||
|
Generate all SYS-created functions required by the Skill for a customer Agent schema:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py sys_functions BOSNDS3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Skill 更新方式
|
||||||
|
|
||||||
|
Skill 仅通过 Gitea Git 仓库更新,不使用 OSS 发布或 `skill_update` 命令。源码修改、提交和推送均在源码仓库完成;已安装目录仅执行拉取与验证:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\qiang\.codex\skills\oracle-jump-query
|
||||||
|
git pull --ff-only origin main
|
||||||
|
python scripts/oracle_skill.py capabilities --json
|
||||||
|
```
|
||||||
|
|
||||||
|
The command reads every SQL file under `references/sys-functions/`, replaces `<AGENT_SCHEMA>`, and only outputs SQL for a DBA to execute. It never writes to the customer database. Any future SYS function must be added to that directory so this command includes it automatically.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"transit_url": "https://ts.henlo.net",
|
||||||
|
"server_id": "RENBEN",
|
||||||
|
"access_token": "",
|
||||||
|
"expires_at": ""
|
||||||
|
}
|
||||||
+482
@@ -0,0 +1,482 @@
|
|||||||
|
# Oracle Jump Query 操作示例
|
||||||
|
|
||||||
|
这份文档用于说明如何向 `Oracle Jump Query` 提问,以及它会调用哪些只读能力完成查询。适合给业务分析、存储过程排查、表结构梳理、权限数据查询等场景使用。
|
||||||
|
|
||||||
|
## 基本原则
|
||||||
|
|
||||||
|
- 只执行只读操作:支持查询元数据和执行 `SELECT`,禁止 `INSERT`、`UPDATE`、`DELETE`、`MERGE`、`TRUNCATE`、`DROP`、`ALTER` 等写操作或结构变更。
|
||||||
|
- 优先给出明确对象:尽量提供 `schema`、表名、存储过程名、用户 ID、`AD_TABLE.ID` 等信息。
|
||||||
|
- 业务词先查字典:遇到“零售单”“采购入库”“调拨单”等业务词时,先从 `AD_TABLE`、`AD_COLUMN`、`AD_REFBYTABLE` 确认表和字段。
|
||||||
|
- 单据统计加状态条件:统计业务单据时,通常需要过滤 `ISACTIVE = 'Y' AND STATUS = '2'`。
|
||||||
|
- 明细表不靠猜:主表到明细表关系应通过 `AD_REFBYTABLE` 查询确认。
|
||||||
|
|
||||||
|
## 常见操作
|
||||||
|
|
||||||
|
### 1. 检查服务状态
|
||||||
|
|
||||||
|
用于确认中转服务和在线 Agent 是否可用。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
检查一下 Oracle Jump Query 服务是否正常,看看有哪些在线 Agent。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py health
|
||||||
|
python scripts/oracle_skill.py servers
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 查询存储过程源码
|
||||||
|
|
||||||
|
用于查看某个存储过程的完整 PL/SQL 源码。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我看一下 BOS.M_RETAIL_SUBMIT 的源码。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py source BOS M_RETAIL_SUBMIT
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 完整分析存储过程
|
||||||
|
|
||||||
|
用于一次性查看源码、依赖对象、相关表结构和触发器。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
完整分析 BOS.M_RETAIL_SUBMIT,包括源码、依赖表、相关表结构和触发器。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py analyze BOS M_RETAIL_SUBMIT
|
||||||
|
```
|
||||||
|
|
||||||
|
适合排查:
|
||||||
|
|
||||||
|
- 提交流程做了哪些校验
|
||||||
|
- 存储过程更新了哪些业务表
|
||||||
|
- 是否调用了其他过程或函数
|
||||||
|
- 哪些触发器可能影响结果
|
||||||
|
|
||||||
|
### 4. 查询过程依赖
|
||||||
|
|
||||||
|
用于单独查看某个过程依赖的表、视图、函数或其他过程。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查一下 BOS.M_RETAIL_SUBMIT 依赖了哪些表和过程。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py deps BOS M_RETAIL_SUBMIT
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 查询表结构
|
||||||
|
|
||||||
|
用于查询字段、类型、索引、行数、注释等表结构信息。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查一下 BOSNDS3.XCX_SO 的表结构。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py describe BOSNDS3 XCX_SO
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 根据业务词找表
|
||||||
|
|
||||||
|
用于从中文业务名称反查系统中的业务表。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我找“零售单”对应哪张表,以及提交过程是什么。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应 SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT ID, NAME, TABLENAME, DESCRIPTION, PROC_SUBMIT
|
||||||
|
FROM AD_TABLE
|
||||||
|
WHERE NAME LIKE '%零售%'
|
||||||
|
OR DESCRIPTION LIKE '%零售%';
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py query "SELECT ID, NAME, TABLENAME, DESCRIPTION, PROC_SUBMIT FROM AD_TABLE WHERE NAME LIKE '%零售%' OR DESCRIPTION LIKE '%零售%'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. 查询字段业务含义
|
||||||
|
|
||||||
|
用于确认字段显示名、业务描述和字段类型。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查一下 M_RETAILITEM 每个字段的业务含义。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应 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;
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py query "SELECT DBNAME, NAME, DESCRIPTION, COLTYPE FROM AD_COLUMN WHERE AD_TABLE_ID = (SELECT ID FROM AD_TABLE WHERE NAME = 'M_RETAILITEM') ORDER BY ORDERNO"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 查询主表和明细表关系
|
||||||
|
|
||||||
|
用于确认主表有哪些明细表,以及明细表通过哪个字段关联主表。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查一下 M_RETAIL 有哪些明细表,外键字段是什么。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应 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';
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py query "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'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. 查询有效已提交单据
|
||||||
|
|
||||||
|
用于统计或分析正式业务数据。单据类数据通常要排除草稿、审批中、作废或删除记录。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查询 2026-05-01 的有效已提交零售单,返回前 10 条。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应 SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT *
|
||||||
|
FROM M_RETAIL
|
||||||
|
WHERE ISACTIVE = 'Y'
|
||||||
|
AND STATUS = '2'
|
||||||
|
AND BILLDATE = 20260501
|
||||||
|
AND ROWNUM <= 10;
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py query "SELECT * FROM M_RETAIL WHERE ISACTIVE = 'Y' AND STATUS = '2' AND BILLDATE = 20260501 AND ROWNUM <= 10"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10. 查询用户数据权限
|
||||||
|
|
||||||
|
用于查看某个用户对某张业务表的数据范围。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
查一下用户 940 对 M_OTHER_INOUT 的数据权限,表 ID 是 12983。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py perm 940 12983
|
||||||
|
```
|
||||||
|
|
||||||
|
返回结果通常包含:
|
||||||
|
|
||||||
|
- `perm_sql`:权限过滤条件
|
||||||
|
- `has_restriction`:是否存在权限限制
|
||||||
|
- 空权限条件:通常表示全权限
|
||||||
|
|
||||||
|
### 11. 执行带权限过滤的查询
|
||||||
|
|
||||||
|
用于按用户权限自动拼接过滤条件后查询数据。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用用户 940 的权限查询 M_OTHER_INOUT,表 ID 是 12983,只看 2026-05-01 的数据。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py qperm 940 12983 "SELECT * FROM M_OTHER_INOUT WHERE BILLDATE = 20260501"
|
||||||
|
```
|
||||||
|
|
||||||
|
系统会自动把 `get_userspermsql` 返回的权限条件追加到查询中。
|
||||||
|
|
||||||
|
### 12. 查询表空间使用情况
|
||||||
|
|
||||||
|
用于查看数据库表空间的整体使用状况,包括总大小、已用、剩余、使用率、是否自动拓展等信息。
|
||||||
|
|
||||||
|
#### 自动降级策略
|
||||||
|
|
||||||
|
脚本按优先级自动尝试 **5 种方式**,无需手动切换:
|
||||||
|
|
||||||
|
| 优先级 | 方式 | 条件 |
|
||||||
|
|:---:|------|------|
|
||||||
|
| 1️⃣ | 直查 `DBA_DATA_FILES` + `DBA_FREE_SPACE` | 用户有 `SELECT_CATALOG_ROLE` |
|
||||||
|
| 2️⃣ | DBA 专用函数 `sys.get_tablespace_usage()` | DBA 已执行 CREATE FUNCTION |
|
||||||
|
| 3️⃣ | 查询 DBA 授权 VIEW `HENLO_TS_USAGE` | DBA 已执行授权 SQL |
|
||||||
|
| 4️⃣ | 查询 `SYS.HENLO_TS_USAGE`(带 sys.前缀) | DBA 以 SYS 身份创建的视图 |
|
||||||
|
| 5️⃣ | 查 `USER_FREE_SPACE` | 始终可用(仅剩余空间) |
|
||||||
|
|
||||||
|
> **经验**:DBA 以 SYS 身份登录创建的 VIEW 归属到 `SYS` schema,需用 `sys.` 前缀才能查到。脚本会自动先试无前缀,再试 `sys.` 前缀。
|
||||||
|
|
||||||
|
#### 无权限时的处理流程
|
||||||
|
|
||||||
|
当用户无 DBA 系统视图权限时,脚本会自动输出以下内容:
|
||||||
|
|
||||||
|
1. 尝试 DBA 专用函数(如果有)
|
||||||
|
2. 尝试 DBA 授权 VIEW(无前缀 → sys.前缀)
|
||||||
|
3. 如果前两步都不存在,输出给 DBA 的授权 SQL
|
||||||
|
4. 回退到 `USER_FREE_SPACE` 查询,显示各表空间的剩余空间
|
||||||
|
|
||||||
|
#### HENLO 环境实际输出(授权前)
|
||||||
|
|
||||||
|
```
|
||||||
|
📊 表空间使用情况(用户视图 -- 无DBA权限,仅显示名称和剩余空间):
|
||||||
|
| 表空间名称 | 剩余MB |
|
||||||
|
| --- | --- |
|
||||||
|
| BOSNDS3 | 518.81 |
|
||||||
|
| BOSNDS3_IDX | 17.56 |
|
||||||
|
| RETAILNDS3 | 9.31 |
|
||||||
|
| RETAILNDS3_IDX | 19 |
|
||||||
|
| SYSAUX | 329.94 |
|
||||||
|
| SYSTEM | 7.19 |
|
||||||
|
| ULOG | 39 |
|
||||||
|
| UNDOTBS1 | 474.38 |
|
||||||
|
| USERS | 2.31 |
|
||||||
|
```
|
||||||
|
|
||||||
|
#### DBA 授权后的实际完整输出
|
||||||
|
|
||||||
|
DBA 以 SYS 执行授权 SQL 后,再次查询自动获得完整信息(脚本自动识别 `SYS.HENLO_TS_USAGE`):
|
||||||
|
|
||||||
|
```
|
||||||
|
📊 表空间使用情况一览(通过 DBA 授权 VIEW):
|
||||||
|
| 表空间名称 | 总大小MB | 已用MB | 剩余MB | 使用率% | 自动拓展 | 最大可拓展MB | 最大使用率% |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| SYSTEM | 1230 | 1222.8 | 7.2 | 99.4 | YES | 32768 | 3.7 |
|
||||||
|
| SYSAUX | 6620 | 6290.1 | 329.9 | 95 | YES | 32768 | 19.2 |
|
||||||
|
| UNDOTBS1 | 505 | 30.6 | 474.4 | 6.1 | YES | 32768 | 0.1 |
|
||||||
|
| BOSNDS3_IDX | 20 | 2.4 | 17.6 | 12.2 | YES | 32768 | 0 |
|
||||||
|
| ULOG | 40 | 1 | 39 | 2.5 | YES | 32768 | 0 |
|
||||||
|
| RETAILNDS3_IDX | 20 | 1 | 19 | 5 | YES | 32768 | 0 |
|
||||||
|
| USERS | 5 | 2.7 | 2.3 | 53.8 | YES | 32768 | 0 |
|
||||||
|
| BOSNDS3 | 10690 | 10171.2 | 518.8 | 95.1 | YES | 32768 | 31 |
|
||||||
|
| RETAILNDS3 | 110 | 100.7 | 9.3 | 91.5 | YES | 32768 | 0.3 |
|
||||||
|
|
||||||
|
**共 9 行**
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ 脚本自动检测到 3 个表空间使用率 ≥ 95% 并输出告警。
|
||||||
|
|
||||||
|
#### 字段解释
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 表空间名称 | Oracle 表空间名称 |
|
||||||
|
| 总大小MB | 当前数据文件总大小(MB) |
|
||||||
|
| 已用MB | 已分配使用的空间(MB) |
|
||||||
|
| 剩余MB | 剩余可用空间(MB) |
|
||||||
|
| 使用率% | (已用 / 总大小)× 100% |
|
||||||
|
| 自动拓展 | YES=数据文件自动扩展,NO=固定大小 |
|
||||||
|
| 最大可拓展MB | 自动扩展后能达到的最大值(MB);不自动扩展时=当前大小 |
|
||||||
|
| 最大使用率% | (已用 / 最大可拓展)× 100%,反映最终可用的百分比 |
|
||||||
|
|
||||||
|
#### 阈值参考
|
||||||
|
|
||||||
|
| 使用率 | 状态 | 建议 |
|
||||||
|
|--------|------|------|
|
||||||
|
| < 80% | ✅ 正常 | 无需处理 |
|
||||||
|
| 80% ~ 95% | 🔶 关注 | 评估未来增长,制定扩容计划 |
|
||||||
|
| ≥ 95% | ⚠️ 紧急 | 尽快扩容或清理空间! |
|
||||||
|
|
||||||
|
> **智能告警规则**:脚本逐行解析表空间数据,自动判断衡量指标。
|
||||||
|
> - `自动拓展=YES`:按 **最大使用率%**(已用/最大可拓展)判断
|
||||||
|
> - `自动拓展=NO`:按当前 **使用率%**(已用/当前总大小)判断
|
||||||
|
>
|
||||||
|
> 例如 SYSTEM 使用率 99.4% 但最大使用率仅 3.7%,说明只是当前数据文件写满而非存储瓶颈,自动扩展后即可释放。
|
||||||
|
|
||||||
|
#### 给 DBA 的授权说明
|
||||||
|
|
||||||
|
当无 DBA 权限时,脚本会**自动输出**以下信息,请交给 DBA 执行:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 以 SYS 身份执行
|
||||||
|
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;
|
||||||
|
```
|
||||||
|
|
||||||
|
> **为什么加 `SYS.` 前缀**:DBA 以 SYS 身份执行 `CREATE VIEW` 时,视图创建在 `SYS` schema 下。`GRANT` 也必须指定 `SYS.` 前缀,否则普通用户无法找到。脚本会自动处理带前缀和不带前缀两种情况。
|
||||||
|
|
||||||
|
> **原理**:Oracle 视图默认使用定义者权限(`DEFINER`),以创建者的 DBA 身份执行。普通用户仅需 `SELECT` 权限,比直接授予 `SELECT_CATALOG_ROLE` 更安全。
|
||||||
|
|
||||||
|
#### 给 AI 的指令
|
||||||
|
|
||||||
|
当用户通过 AI 查询表空间时,流程:
|
||||||
|
|
||||||
|
1. 自动调用 `python oracle_skill.py tablespace`
|
||||||
|
2. 脚本自动尝试 5 层降级
|
||||||
|
3. 如果全部权限方案不可用,输出授权 SQL + 回退到剩余空间查询
|
||||||
|
4. 将授权 SQL 发送给用户,让其联系 DBA 执行
|
||||||
|
5. DBA 执行后,再次查询即可自动使用完整信息
|
||||||
|
6. 注意:DBA 以 SYS 执行时,VIEW 会创建在 `SYS` schema 下,GRANT 也需带 `SYS.` 前缀
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我查一下数据库表空间使用情况
|
||||||
|
查看表空间
|
||||||
|
表空间够不够用
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py tablespace
|
||||||
|
# 或
|
||||||
|
python scripts/oracle_skill.py tablespaces
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13. 生成 NL2SQL 字典
|
||||||
|
|
||||||
|
用于为自然语言转 SQL 准备 schema 字典。
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
给 BOSNDS3 的 RETAIL 业务域生成 NL2SQL schema 字典。
|
||||||
|
```
|
||||||
|
|
||||||
|
对应命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py nl2sql BOSNDS3 RETAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
## 典型完整流程
|
||||||
|
|
||||||
|
### 流程一:分析一个业务提交流程
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我分析零售单提交逻辑,从业务表、提交过程、依赖表到关键校验都梳理出来。
|
||||||
|
```
|
||||||
|
|
||||||
|
推荐执行顺序:
|
||||||
|
|
||||||
|
1. 从 `AD_TABLE` 查询“零售单”对应主表和 `PROC_SUBMIT`。
|
||||||
|
2. 使用 `source` 或 `analyze` 查询提交过程源码。
|
||||||
|
3. 使用 `deps` 查询依赖对象。
|
||||||
|
4. 使用 `describe` 查询关键表结构。
|
||||||
|
5. 结合源码解释提交校验、状态变更、库存或财务影响。
|
||||||
|
|
||||||
|
### 流程二:构造一条可靠统计 SQL
|
||||||
|
|
||||||
|
用户可以这样问:
|
||||||
|
|
||||||
|
```text
|
||||||
|
我要统计 2026 年 5 月零售销售额,帮我确认主表、明细表、金额字段并生成 SQL。
|
||||||
|
```
|
||||||
|
|
||||||
|
推荐执行顺序:
|
||||||
|
|
||||||
|
1. 查 `AD_TABLE`,确认零售主表。
|
||||||
|
2. 查 `AD_REFBYTABLE`,确认零售明细表。
|
||||||
|
3. 查 `AD_COLUMN`,确认数量、金额、日期、门店字段。
|
||||||
|
4. 生成 SQL 时加入 `ISACTIVE = 'Y' AND STATUS = '2'`。
|
||||||
|
5. 如需按用户权限过滤,使用 `qperm`。
|
||||||
|
|
||||||
|
示例 SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT r.C_STORE_ID,
|
||||||
|
SUM(i.TOT_AMT_ACTUAL) AS SALE_AMOUNT
|
||||||
|
FROM M_RETAIL r
|
||||||
|
JOIN M_RETAILITEM i ON i.M_RETAIL_ID = r.ID
|
||||||
|
WHERE r.ISACTIVE = 'Y'
|
||||||
|
AND r.STATUS = '2'
|
||||||
|
AND r.BILLDATE BETWEEN 20260501 AND 20260531
|
||||||
|
GROUP BY r.C_STORE_ID;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 排查建议
|
||||||
|
|
||||||
|
- `agent not found`:先执行 `servers`,确认 Agent 是否在线。
|
||||||
|
- 查询超时:缩小查询范围,或对 `analyze` 类命令增加超时时间。
|
||||||
|
- 查不到业务表:先扩大 `AD_TABLE` 的关键字范围,再结合 `DESCRIPTION` 判断。
|
||||||
|
- 字段含义不明确:优先查 `AD_COLUMN.DESCRIPTION`,不要只凭字段名猜。
|
||||||
|
- 统计结果异常:检查是否遗漏 `ISACTIVE`、`STATUS`、日期范围、门店权限等条件。
|
||||||
|
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
# BOS 数据字典与系统地图
|
||||||
|
|
||||||
|
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
|
||||||
|
|
||||||
|
## 数据字典(AD_TABLE / AD_COLUMN / AD_REFBYTABLE)
|
||||||
|
|
||||||
|
### 通用查询规则
|
||||||
|
|
||||||
|
处理 BOS 业务单据(如零售单、采购单、调拨单、销售单等)时,必须遵循以下查询路径:
|
||||||
|
|
||||||
|
**1. 术语 → 表名(AD_TABLE)**
|
||||||
|
|
||||||
|
遇到不懂的业务术语(如"零售单提交"、"采购入库"),先查 AD_TABLE:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT ID,
|
||||||
|
NAME,
|
||||||
|
TABLENAME,
|
||||||
|
DESCRIPTION,
|
||||||
|
MASK,
|
||||||
|
PROC_SUBMIT,
|
||||||
|
HAS_TRIG_BD,
|
||||||
|
TRIG_BD,
|
||||||
|
HAS_TRIG_AC,
|
||||||
|
TRIG_AC,
|
||||||
|
HAS_TRIG_AM,
|
||||||
|
TRIG_AM
|
||||||
|
FROM AD_TABLE
|
||||||
|
WHERE NAME LIKE '%关键词%' OR DESCRIPTION LIKE '%关键词%'
|
||||||
|
```
|
||||||
|
|
||||||
|
- `NAME`:表名(英文,如 M_RETAIL)
|
||||||
|
- `DESCRIPTION`:业务说明(中文,如"零售单")
|
||||||
|
- `MASK`:表支持的业务动作代码,可由多个字母组成:`A`=新增、`M`=修改、`D`=删除、`Q`=查询、`U`=取消提交、`V`=作废、`S`=提交。分析表单功能、按钮或流程时,应以该字段实际包含的代码判断该表声明支持的动作,不要仅凭表名推断。
|
||||||
|
- `PROC_SUBMIT`:该单据的提交存储过程名称(非空即代表该表有提交逻辑)
|
||||||
|
- `HAS_TRIG_BD` / `TRIG_BD`:`HAS_TRIG_BD='Y'` 时,`TRIG_BD` 是删除前触发的存储过程,简称 `bd`
|
||||||
|
- `HAS_TRIG_AC` / `TRIG_AC`:`HAS_TRIG_AC='Y'` 时,`TRIG_AC` 是新增后触发的存储过程,简称 `ac`
|
||||||
|
- `HAS_TRIG_AM` / `TRIG_AM`:`HAS_TRIG_AM='Y'` 时,`TRIG_AM` 是修改后触发的存储过程,简称 `am`
|
||||||
|
|
||||||
|
分析 BOS 业务表时,`PROC_SUBMIT` 和这些 `TRIG_*` 字段都属于业务逻辑入口。不要只看物理触发器或过程源码;先从 `AD_TABLE` 判断表单事件过程是否配置,再按过程名继续查源码、依赖和影响表。
|
||||||
|
|
||||||
|
**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`、`MASK`(读写业务动作)、`PROC_SUBMIT`、`HAS_TRIG_BD/TRIG_BD`、`HAS_TRIG_AC/TRIG_AC`、`HAS_TRIG_AM/TRIG_AM`、`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.MASK,
|
||||||
|
t.PROC_SUBMIT,
|
||||||
|
t.HAS_TRIG_BD,
|
||||||
|
t.TRIG_BD,
|
||||||
|
t.HAS_TRIG_AC,
|
||||||
|
t.TRIG_AC,
|
||||||
|
t.HAS_TRIG_AM,
|
||||||
|
t.TRIG_AM,
|
||||||
|
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`:表归属、提交过程、表单事件过程(bd/ac/am)、权限目录。
|
||||||
|
- `AD_COLUMN`:字段业务含义和值域。
|
||||||
|
- `AD_REFBYTABLE`:明细表和外键列。
|
||||||
|
- `AD_ACTION`:按钮和动作脚本。
|
||||||
|
- `ALL_TRIGGERS` / `USER_OBJECTS`:触发器、同名前缀过程或函数。
|
||||||
|
|
||||||
|
注意:这套元数据能还原 BOS 业务结构,不等于完整物理数据库模型。复杂过程内部逻辑、动态 SQL、触发器副作用和前端特殊页面仍需要结合源码继续分析。
|
||||||
|
|
||||||
|
|
||||||
|
## 数据权限系统
|
||||||
|
|
||||||
|
### 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 | 门店盘点日 |
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
# 命令、登录与调用方式
|
||||||
|
|
||||||
|
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
通过中转服务(Transit Server)查询远程 Oracle 数据库服务器的存储过程元数据,无需开放数据库端口。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
AI Skill → HTTP → Transit Server (:6357) → WebSocket → Agent → Oracle
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
1. 中转服务已部署并运行(默认地址: https://ts.henlo.net)
|
||||||
|
2. Agent 已部署到数据库服务器并连接中转服务
|
||||||
|
3. 本地已安装 Python 依赖:`pip install requests`
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
配置文件位于技能目录下:`D:\work\恒诺\长期支持\跳板查询\demo\ai-skill\config.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"transit_url": "https://ts.henlo.net",
|
||||||
|
"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|clientName>` | 按 code 或名称切换 client;找不到时自动刷新 client 列表 | `python oracle_skill.py switch HENLO` |
|
||||||
|
| `clients` | 无感刷新当前用户最新授权的 client 列表及在线状态;只使用当前登录 token | `python oracle_skill.py clients` |
|
||||||
|
| `agent_update [clientCode] [timeout]` | 触发客户 Agent 自动更新,升级包由 Agent 从 OSS 下载 | `python oracle_skill.py agent_update HENLO 300` |
|
||||||
|
| `awr_status [clientCode]` | 查看 AWR 授权和可用状态,不生成报告 | `python oracle_skill.py awr_status WEIRUI` |
|
||||||
|
| `awr_list [clientCode]` | 列出 Agent 已生成的 AWR 报告 | `python oracle_skill.py awr_list WEIRUI` |
|
||||||
|
| `awr_download <clientCode> <yyyyMMdd>` | 下载 Agent 原始 AWR HTML | `python oracle_skill.py awr_download WEIRUI 20260720` |
|
||||||
|
| `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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 分析报告内容
|
||||||
|
|
||||||
|
`analyze` 命令返回完整分析报告,包含:
|
||||||
|
- **源码**:完整 PL/SQL 代码
|
||||||
|
- **依赖**:涉及的表、视图、嵌套存储过程/函数
|
||||||
|
- **表结构**:相关表的列信息(列名、类型、长度、可空)
|
||||||
|
- **触发器**:相关表上的触发器列表
|
||||||
|
|
||||||
|
## 直接 HTTP 调用
|
||||||
|
|
||||||
|
不通过脚本,直接调用中转服务 API:
|
||||||
|
|
||||||
|
### 分析存储过程
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://ts.henlo.net/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 https://ts.henlo.net/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 字典 |
|
||||||
|
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
| 问题 | 排查 |
|
||||||
|
|------|------|
|
||||||
|
| `无法连接中转服务` | 检查 transit_url 地址和端口 |
|
||||||
|
| `agent not found` | 确认 Agent 已启动并连接成功 |
|
||||||
|
| `请求超时` | Agent 可能卡住,增大 timeout 参数 |
|
||||||
|
| Oracle 连接失败 | 检查 Agent 的 config.json 中 Oracle 配置 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Agent 版本查询
|
||||||
|
|
||||||
|
自然语言可说:查询所有客户的 Agent 版本号,或查询指定客户版本号。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py version agent HENLO RENBEN --json
|
||||||
|
python scripts/oracle_skill.py version agent --all --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Skill Git 更新流程
|
||||||
|
|
||||||
|
Skill 使用 Gitea Git 仓库作为唯一更新来源,不使用 OSS 或 `skill_update` 命令。源码仓库完成修改、提交和推送后,在已安装目录执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\qiang\.codex\skills\oracle-jump-query
|
||||||
|
git pull --ff-only origin main
|
||||||
|
python scripts/oracle_skill.py capabilities --json
|
||||||
|
```
|
||||||
|
|
||||||
|
在线 Agent 返回当前版本;离线客户返回最后一次心跳版本和离线状态。
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
```
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
# 表空间、服务器体检与 Oracle 运维监控
|
||||||
|
|
||||||
|
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
|
||||||
|
|
||||||
|
## 表空间查询
|
||||||
|
|
||||||
|
查询 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 确认字段名和值域
|
||||||
|
|
||||||
|
|
||||||
|
## 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 的备份情况”、“生成巡检 HTML”这类请求时,统一使用归档巡检入口:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO
|
||||||
|
python scripts/oracle_skill.py inspection_latest HENLO
|
||||||
|
python scripts/oracle_skill.py inspection_latest HENLO --refresh
|
||||||
|
python scripts/oracle_skill.py inspection_report HENLO --markdown
|
||||||
|
```
|
||||||
|
|
||||||
|
`inspection_report` 会实时请求 Agent 的 `inspection_report_data` 并归档到 `TS_INSPECTION_REPORT`;`inspection_latest` 默认读取最近一次历史快照,加 `--refresh` 时重新采集并返回。三个巡检命令成功后均默认使用固定格式模板导出 HTML,保证每次结构一致,只替换数据内容;增加 `--markdown`(或 `--md`)可同时导出固定格式 Markdown。
|
||||||
|
|
||||||
|
当前巡检数据包括服务器 IP、操作系统版本、启动时间、运行时长、关键进程、TCP 连接状态、Oracle ACTIVE/总会话数,以及 Buffer Cache、Library Cache 命中率。TCP 连接统计与 Oracle 会话数是两个独立指标,不混合计算。
|
||||||
|
|
||||||
|
## AWR 报告
|
||||||
|
|
||||||
|
AWR 仅在客户 DBA 明确确认 Diagnostics Pack 授权,且 Agent 同时配置 `awr.enabled=true`、`awr.license_confirmed=true` 后启用。Skill 不执行授权 SQL,不随日常巡检隐式生成 AWR,也不在本地重新渲染 HTML。
|
||||||
|
|
||||||
|
当 `awr_status` 返回 `permission_required` 时,Skill 会从目标 Agent 的 `version` action 解析实际 Oracle schema,并返回已替换账号的 `HENLO_AWR_EXPORT` 创建/授权 SQL,供 DBA 在确认 Diagnostics Pack 授权后手工执行。无法解析有效 schema 时停止输出 SQL,不保留 `<AGENT_SCHEMA>` 占位符。
|
||||||
|
|
||||||
|
`HENLO_AWR_EXPORT` v1.1 对 Oracle AWR 报告流中的空输出行进行保护,避免把 `LENGTH(NULL)` 传给 `DBMS_LOB.WRITEAPPEND` 触发 `ORA-06502`;每个输出行后追加换行,保持 HTML 原始行边界。若旧版函数已生成 `skipped` 元数据,先由 DBA 手工升级函数,再升级并重启 Agent `1.8.12+` 触发一次恢复重试。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py awr_status WEIRUI
|
||||||
|
python scripts/oracle_skill.py awr_list WEIRUI
|
||||||
|
python scripts/oracle_skill.py awr_download WEIRUI 20260720
|
||||||
|
```
|
||||||
|
|
||||||
|
Agent 默认在东八区每日 01:00 尝试生成前一日 AWR,并按配置保留本地文件。`awr_download` 经 transit-server 权限校验后原样下载 Agent 文件,默认保存到 `outputs/AWR-<client>-<date>.html`,最大 50 MB。AWR 失败不影响服务器基础巡检、Oracle 会话和缓存命中率等常规项目。
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# 生产查询限制与常用业务规则
|
||||||
|
|
||||||
|
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
|
||||||
|
|
||||||
|
## 生产查询限制
|
||||||
|
|
||||||
|
直接查询业务表时,必须带明确过滤条件,不要直接查询整表。生产环境数据量很大,`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`。
|
||||||
|
- 商品明细表中常见的三个商品维度字段:
|
||||||
|
- `M_PRODUCT_ID`:款号 / 商品,关联 `M_PRODUCT.ID`。
|
||||||
|
- `M_PRODUCTALIAS_ID`:条码 / SKU,关联 `M_PRODUCT_ALIAS.ID`。
|
||||||
|
- `M_ATTRIBUTESETINSTANCE_ID`:色码属性 ASI,关联 `M_ATTRIBUTESETINSTANCE.ID`。
|
||||||
|
- `M_ATTRIBUTESETINSTANCE` 用于记录商品色码属性:
|
||||||
|
- `VALUE1`:颜色名称。
|
||||||
|
- `VALUE1_CODE`:颜色编号。
|
||||||
|
- `VALUE1_ID`:颜色表 ID,关联 `M_COLOR.ID`。
|
||||||
|
- `VALUE2`:尺码名称。
|
||||||
|
- `VALUE2_CODE`:尺码编号。
|
||||||
|
- `VALUE2_ID`:尺码表 ID,关联 `M_SIZE.ID`。
|
||||||
|
- 如果涉及库存查询,通常查询 `V_FA_STORAGE` 视图。
|
||||||
|
- `V_FA_STORAGE` 数据量很大,必须同时带店仓条件和款号/条码条件,不允许只查整张库存视图。
|
||||||
|
|
||||||
|
库存查询过滤规则:
|
||||||
|
|
||||||
|
- 店仓条件应优先使用门店、仓库、店仓 ID 或店仓编码等字段,先通过 `describe` 或 `AD_COLUMN` 确认实际字段名。
|
||||||
|
- 款号条件走 `M_PRODUCT`;条码/SKU 条件走 `M_PRODUCT_ALIAS`。
|
||||||
|
- 颜色、尺码、色码属性条件优先走 `M_ATTRIBUTESETINSTANCE`,再分别通过 `VALUE1_ID` 关联 `M_COLOR`、通过 `VALUE2_ID` 关联 `M_SIZE`。
|
||||||
|
- 通过条码查库存时,应先用 `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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 新增业务记录 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`,按该表配置的提交存储过程理解提交逻辑。
|
||||||
|
- 生成提交 SQL 或说明提交流程时,如果已知 `userId`,调用 `AD_TABLE.PROC_SUBMIT` 前需要先更新主单据修改人和修改时间,再调用提交存储过程;修改人、修改时间字段名必须先通过 `AD_COLUMN` 或表结构确认,不要凭经验硬写字段名。
|
||||||
|
|
||||||
|
查询 `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
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# HENLO Oracle SYS functions
|
||||||
|
|
||||||
|
These scripts are DBA-operated SQL artifacts. The Agent never executes `GRANT`, DDL, or any write operation.
|
||||||
|
|
||||||
|
Apply order:
|
||||||
|
|
||||||
|
1. `henlo_ora_monitor.sql`
|
||||||
|
2. `henlo_awr_export.sql` only when the DBA has confirmed Diagnostics Pack licensing and AWR access.
|
||||||
|
|
||||||
|
Replace `<AGENT_SCHEMA>` with the read-only Oracle account used by the Agent. Record the applied version in the database change log.
|
||||||
|
|
||||||
|
Generate the complete current bundle with:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/oracle_skill.py sys_functions <AGENT_SCHEMA>
|
||||||
|
```
|
||||||
|
|
||||||
|
The command reads every `.sql` file in this directory and replaces the schema placeholder. Any new SYS function must be added here so it is automatically included.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- HENLO_AWR_EXPORT v1.1
|
||||||
|
-- DBA only. Requires Diagnostics Pack authorization.
|
||||||
|
CREATE OR REPLACE FUNCTION SYS.HENLO_AWR_EXPORT(
|
||||||
|
P_BEGIN_SNAP NUMBER, P_END_SNAP NUMBER, P_DBID NUMBER DEFAULT NULL, P_INSTANCE NUMBER DEFAULT NULL
|
||||||
|
) RETURN CLOB AUTHID DEFINER IS
|
||||||
|
L_DBID NUMBER := P_DBID;
|
||||||
|
L_INSTANCE NUMBER := P_INSTANCE;
|
||||||
|
L_HTML CLOB;
|
||||||
|
BEGIN
|
||||||
|
IF L_DBID IS NULL THEN SELECT DBID INTO L_DBID FROM V$DATABASE; END IF;
|
||||||
|
IF L_INSTANCE IS NULL THEN SELECT INSTANCE_NUMBER INTO L_INSTANCE FROM V$INSTANCE; END IF;
|
||||||
|
DBMS_LOB.CREATETEMPORARY(L_HTML, TRUE);
|
||||||
|
FOR R IN (SELECT OUTPUT FROM TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_HTML(L_DBID, L_INSTANCE, P_BEGIN_SNAP, P_END_SNAP))) LOOP
|
||||||
|
IF R.OUTPUT IS NOT NULL THEN
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_HTML, LENGTH(R.OUTPUT), R.OUTPUT);
|
||||||
|
END IF;
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_HTML, 1, CHR(10));
|
||||||
|
END LOOP;
|
||||||
|
RETURN L_HTML;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
GRANT EXECUTE ON SYS.HENLO_AWR_EXPORT TO <AGENT_SCHEMA>;
|
||||||
|
GRANT SELECT ON SYS.DBA_HIST_SNAPSHOT TO <AGENT_SCHEMA>;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
-- HENLO_ORA_MONITOR v3.0
|
||||||
|
-- DBA only. Replace <AGENT_SCHEMA> before execution.
|
||||||
|
-- Returns JSON in a CLOB. This return contract is required by Agent 1.8.6+.
|
||||||
|
CREATE OR REPLACE FUNCTION SYS.HENLO_ORA_MONITOR(
|
||||||
|
P_ITEM VARCHAR2, P_MINUTES NUMBER DEFAULT 60, P_TOP_N NUMBER DEFAULT 20
|
||||||
|
) RETURN CLOB AUTHID DEFINER IS
|
||||||
|
L_ITEM VARCHAR2(64) := LOWER(TRIM(P_ITEM));
|
||||||
|
L_SQL VARCHAR2(32767);
|
||||||
|
L_TITLE VARCHAR2(200);
|
||||||
|
L_RESULT CLOB;
|
||||||
|
L_ROWS CLOB;
|
||||||
|
L_COLS CLOB;
|
||||||
|
L_CUR INTEGER;
|
||||||
|
L_COL_COUNT INTEGER;
|
||||||
|
L_DESC DBMS_SQL.DESC_TAB2;
|
||||||
|
L_VALUE VARCHAR2(4000);
|
||||||
|
L_ROW_COUNT NUMBER := 0;
|
||||||
|
L_TOP_N NUMBER := LEAST(GREATEST(NVL(P_TOP_N, 20), 1), 100);
|
||||||
|
L_EXEC INTEGER;
|
||||||
|
|
||||||
|
FUNCTION ESC(P_TEXT VARCHAR2) RETURN VARCHAR2 IS
|
||||||
|
BEGIN
|
||||||
|
IF P_TEXT IS NULL THEN RETURN ''; END IF;
|
||||||
|
RETURN REPLACE(REPLACE(REPLACE(REPLACE(P_TEXT, '\', '\\'), '"', '\"'), CHR(13), ' '), CHR(10), ' ');
|
||||||
|
END;
|
||||||
|
|
||||||
|
PROCEDURE APPEND_TEXT(P_TEXT VARCHAR2) IS
|
||||||
|
BEGIN
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_RESULT, LENGTH(P_TEXT), P_TEXT);
|
||||||
|
END;
|
||||||
|
BEGIN
|
||||||
|
IF L_ITEM = 'capabilities' THEN
|
||||||
|
L_TITLE := 'Monitor capabilities';
|
||||||
|
L_SQL := 'SELECT ''3.0'' function_version, ''active_slow_sql,history_top_sql,fullscan_sql,plan_heavy_sql,blocking_locks,long_transactions,inactive_sessions,session_counts,cache_hit,datafiles,undo,memory,io_waits,background_process,ora_errors,invalid_objects,tablespace,instance_status,awr_snapshots'' supported_items FROM dual';
|
||||||
|
ELSIF L_ITEM = 'instance_status' THEN
|
||||||
|
L_TITLE := 'Instance status';
|
||||||
|
L_SQL := 'SELECT i.status, d.open_mode, d.log_mode, TO_CHAR(i.startup_time,''YYYY-MM-DD HH24:MI:SS'') startup_time, i.instance_name FROM v$instance i CROSS JOIN v$database d';
|
||||||
|
ELSIF L_ITEM = 'active_slow_sql' THEN
|
||||||
|
L_TITLE := 'Active slow SQL';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT s.sid, s.serial# serial_no, s.username, s.machine, s.program, q.sql_id, SUBSTR(q.sql_text,1,500) sql_text, s.last_call_et elapsed_sec, s.status, s.event, s.wait_class FROM v$session s LEFT JOIN v$sql q ON s.sql_id=q.sql_id WHERE s.status=''ACTIVE'' AND s.type<>''BACKGROUND'' ORDER BY s.last_call_et DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'history_top_sql' THEN
|
||||||
|
L_TITLE := 'AWR top SQL';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT s.sql_id, ROUND(SUM(s.elapsed_time_delta)/1000000,2) total_elapsed_s, ROUND(SUM(s.cpu_time_delta)/1000000,2) total_cpu_s, SUM(s.executions_delta) executions, SUBSTR(MAX(t.sql_text),1,500) sql_text FROM dba_hist_sqlstat s JOIN dba_hist_snapshot sn ON s.snap_id=sn.snap_id AND s.dbid=sn.dbid AND s.instance_number=sn.instance_number JOIN dba_hist_sqltext t ON s.sql_id=t.sql_id AND s.dbid=t.dbid WHERE sn.begin_interval_time>=SYSDATE-(NVL(' || NVL(P_MINUTES,60) || ',60)/1440) GROUP BY s.sql_id ORDER BY total_elapsed_s DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'fullscan_sql' THEN
|
||||||
|
L_TITLE := 'Possible full scan SQL';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT sql_id, executions, rows_processed, SUBSTR(sql_text,1,500) sql_text FROM v$sql WHERE executions>100 AND LOWER(sql_text) LIKE ''%select%from%'' AND LOWER(sql_text) NOT LIKE ''%where%'' ORDER BY rows_processed DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'plan_heavy_sql' THEN
|
||||||
|
L_TITLE := 'High buffer SQL';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT sql_id, plan_hash_value, buffer_gets, disk_reads, executions, ROUND(buffer_gets/DECODE(executions,0,1,executions),2) avg_buffer_gets FROM v$sql WHERE executions>0 ORDER BY buffer_gets DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'blocking_locks' THEN
|
||||||
|
L_TITLE := 'Blocking locks';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT l.sid blocker_sid, s1.serial# blocker_serial, s1.username blocker_user, w.sid waiter_sid, s2.serial# waiter_serial, s2.username waiter_user, l.lmode locked_mode, s2.event wait_event FROM v$lock l LEFT JOIN v$session s1 ON l.sid=s1.sid LEFT JOIN v$lock w ON l.id1=w.id1 AND l.id2=w.id2 AND w.request>0 LEFT JOIN v$session s2 ON w.sid=s2.sid WHERE l.block=1) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'long_transactions' THEN
|
||||||
|
L_TITLE := 'Long transactions';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT s.sid, s.serial# serial_no, s.username, t.start_time, t.used_ublk, t.used_urec, s.sql_id FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr ORDER BY t.start_time) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'inactive_sessions' THEN
|
||||||
|
L_TITLE := 'Inactive sessions';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT sid, serial# serial_no, username, machine, program, last_call_et FROM v$session WHERE status=''INACTIVE'' AND last_call_et>1800 ORDER BY last_call_et DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'session_counts' THEN
|
||||||
|
L_TITLE := 'Session counts';
|
||||||
|
L_SQL := 'SELECT SUM(CASE WHEN status=''ACTIVE'' AND type<>''BACKGROUND'' THEN 1 ELSE 0 END) active_sessions, SUM(CASE WHEN type<>''BACKGROUND'' THEN 1 ELSE 0 END) total_sessions FROM v$session';
|
||||||
|
ELSIF L_ITEM = 'cache_hit' THEN
|
||||||
|
L_TITLE := 'Cache hit ratio';
|
||||||
|
L_SQL := 'SELECT ''BUFFER_CACHE'' metric, ROUND((1-pr.value/NULLIF(cg.value+bg.value,0))*100,2) hit_percent FROM v$sysstat pr,v$sysstat cg,v$sysstat bg WHERE pr.name=''physical reads'' AND cg.name=''consistent gets'' AND bg.name=''db block gets'' UNION ALL SELECT ''LIBRARY_CACHE'',ROUND(SUM(pinhits)/NULLIF(SUM(pins),0)*100,2) FROM v$librarycache';
|
||||||
|
ELSIF L_ITEM = 'datafiles' THEN
|
||||||
|
L_TITLE := 'Datafiles';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT file_name,tablespace_name,ROUND(bytes/1024/1024/1024,2) curr_gb,ROUND(maxbytes/1024/1024/1024,2) max_gb,ROUND(bytes/DECODE(maxbytes,0,bytes,maxbytes)*100,2) file_used_pct FROM dba_data_files WHERE maxbytes>0 ORDER BY file_used_pct DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'undo' THEN
|
||||||
|
L_TITLE := 'Undo usage';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT tablespace_name,status,ROUND(SUM(bytes)/1024/1024/1024,2) gb FROM dba_undo_extents GROUP BY tablespace_name,status ORDER BY gb DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'memory' THEN
|
||||||
|
L_TITLE := 'SGA and PGA';
|
||||||
|
L_SQL := 'SELECT metric,value_mb FROM (SELECT ''PGA_USED'' metric,ROUND(SUM(pga_used_mem)/1024/1024,2) value_mb FROM v$process UNION ALL SELECT ''PGA_ALLOC'',ROUND(SUM(pga_alloc_mem)/1024/1024,2) FROM v$process UNION ALL SELECT component,ROUND(current_size/1024/1024,2) FROM v$sga_dynamic_components) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'io_waits' THEN
|
||||||
|
L_TITLE := 'IO waits';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT event,wait_class,total_waits,ROUND(time_waited/100,2) time_waited_sec,ROUND(average_wait/100,2) avg_wait_ms FROM v$system_event WHERE wait_class IN (''User I/O'',''System I/O'') ORDER BY time_waited_sec DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'background_process' THEN
|
||||||
|
L_TITLE := 'Background processes';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT pname,status,program,spid FROM v$process WHERE background=1 ORDER BY pname) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'ora_errors' THEN
|
||||||
|
L_TITLE := 'Recent ORA errors';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT originating_timestamp origin_timestamp,SUBSTR(message_text,1,1000) message_text FROM v$diag_alert_ext WHERE originating_timestamp>=SYSTIMESTAMP-NUMTODSINTERVAL(NVL(' || NVL(P_MINUTES,60) || ',60),''MINUTE'') AND message_text LIKE ''%ORA-%'' ORDER BY originating_timestamp DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'invalid_objects' THEN
|
||||||
|
L_TITLE := 'Invalid objects';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT object_type,owner,object_name,status FROM dba_objects WHERE status<>''VALID'' ORDER BY owner,object_type,object_name) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'tablespace' THEN
|
||||||
|
L_TITLE := 'Tablespace usage';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT df.tablespace_name,ROUND(df.total_bytes/1024/1024/1024,2) total_gb,ROUND((df.total_bytes-NVL(fs.free_bytes,0))/1024/1024/1024,2) used_gb,ROUND(NVL(fs.free_bytes,0)/1024/1024/1024,2) free_gb,ROUND((df.total_bytes-NVL(fs.free_bytes,0))/df.total_bytes*100,2) used_pct FROM (SELECT tablespace_name,SUM(bytes) total_bytes FROM dba_data_files GROUP BY tablespace_name) df LEFT JOIN (SELECT tablespace_name,SUM(bytes) free_bytes FROM dba_free_space GROUP BY tablespace_name) fs ON df.tablespace_name=fs.tablespace_name ORDER BY used_pct DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSIF L_ITEM = 'awr_snapshots' THEN
|
||||||
|
L_TITLE := 'AWR snapshots';
|
||||||
|
L_SQL := 'SELECT * FROM (SELECT snap_id,dbid,instance_number,begin_interval_time,end_interval_time FROM dba_hist_snapshot ORDER BY snap_id DESC) WHERE ROWNUM<=' || L_TOP_N;
|
||||||
|
ELSE
|
||||||
|
RETURN '{"success":false,"item":"' || ESC(L_ITEM) || '","title":"Unknown","status":"failed","columns":[],"rows":[],"warnings":[],"error":"unknown item"}';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DBMS_LOB.CREATETEMPORARY(L_RESULT, TRUE);
|
||||||
|
DBMS_LOB.CREATETEMPORARY(L_ROWS, TRUE);
|
||||||
|
DBMS_LOB.CREATETEMPORARY(L_COLS, TRUE);
|
||||||
|
L_CUR := DBMS_SQL.OPEN_CURSOR;
|
||||||
|
DBMS_SQL.PARSE(L_CUR, L_SQL, DBMS_SQL.NATIVE);
|
||||||
|
DBMS_SQL.DESCRIBE_COLUMNS2(L_CUR, L_COL_COUNT, L_DESC);
|
||||||
|
FOR I IN 1..L_COL_COUNT LOOP
|
||||||
|
DBMS_SQL.DEFINE_COLUMN(L_CUR, I, L_VALUE, 4000);
|
||||||
|
IF I > 1 THEN DBMS_LOB.WRITEAPPEND(L_COLS, 1, ','); END IF;
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_COLS, LENGTH(L_DESC(I).COL_NAME) + 2, '"' || ESC(L_DESC(I).COL_NAME) || '"');
|
||||||
|
END LOOP;
|
||||||
|
L_EXEC := DBMS_SQL.EXECUTE(L_CUR);
|
||||||
|
WHILE DBMS_SQL.FETCH_ROWS(L_CUR) > 0 LOOP
|
||||||
|
L_ROW_COUNT := L_ROW_COUNT + 1;
|
||||||
|
IF L_ROW_COUNT > 1 THEN DBMS_LOB.WRITEAPPEND(L_ROWS, 1, ','); END IF;
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_ROWS, 1, '[');
|
||||||
|
FOR I IN 1..L_COL_COUNT LOOP
|
||||||
|
DBMS_SQL.COLUMN_VALUE(L_CUR, I, L_VALUE);
|
||||||
|
IF I > 1 THEN DBMS_LOB.WRITEAPPEND(L_ROWS, 1, ','); END IF;
|
||||||
|
L_VALUE := '"' || ESC(SUBSTR(L_VALUE, 1, 1000)) || '"';
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_ROWS, LENGTH(L_VALUE), L_VALUE);
|
||||||
|
END LOOP;
|
||||||
|
DBMS_LOB.WRITEAPPEND(L_ROWS, 1, ']');
|
||||||
|
END LOOP;
|
||||||
|
DBMS_SQL.CLOSE_CURSOR(L_CUR);
|
||||||
|
|
||||||
|
APPEND_TEXT('{"success":true,"item":"' || ESC(L_ITEM) || '","title":"' || ESC(L_TITLE) || '","status":"normal","columns":[');
|
||||||
|
DBMS_LOB.APPEND(L_RESULT, L_COLS);
|
||||||
|
APPEND_TEXT('],"rows":[');
|
||||||
|
DBMS_LOB.APPEND(L_RESULT, L_ROWS);
|
||||||
|
APPEND_TEXT('],"warnings":[]}');
|
||||||
|
RETURN L_RESULT;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
IF DBMS_SQL.IS_OPEN(L_CUR) THEN DBMS_SQL.CLOSE_CURSOR(L_CUR); END IF;
|
||||||
|
RETURN '{"success":false,"item":"' || ESC(L_ITEM) || '","title":"' || ESC(NVL(L_TITLE,L_ITEM)) || '","status":"failed","columns":[],"rows":[],"warnings":[],"error":"' || ESC(SQLERRM) || '"}';
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
GRANT EXECUTE ON SYS.HENLO_ORA_MONITOR TO <AGENT_SCHEMA>;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"transit_url": "https://ts.henlo.net",
|
||||||
|
"server_id": "",
|
||||||
|
"access_token": "",
|
||||||
|
"expires_at": "",
|
||||||
|
"user_name": "",
|
||||||
|
"client_code": "",
|
||||||
|
"client_title": "",
|
||||||
|
"client_list": []
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,289 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Oracle Jump Query - AI Skill
|
||||||
|
通过中转服务查询远程 Oracle 存储过程元数据
|
||||||
|
|
||||||
|
使用方式:
|
||||||
|
python oracle_skill.py analyze BOS M_RETAIL_SUBMIT
|
||||||
|
python oracle_skill.py list BOS
|
||||||
|
python oracle_skill.py source BOS M_RETAIL_SUBMIT
|
||||||
|
python oracle_skill.py deps BOS M_RETAIL_SUBMIT
|
||||||
|
python oracle_skill.py tables BOS M_RETAIL_SUBMIT
|
||||||
|
python oracle_skill.py servers
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 配置(修改为你的中转服务地址和认证信息)
|
||||||
|
# ============================================================
|
||||||
|
TRANSIT_URL = "http://bos.henlo.net:6357"
|
||||||
|
AUTH_TOKEN = ""
|
||||||
|
DEFAULT_SERVER_ID = "server-001"
|
||||||
|
|
||||||
|
|
||||||
|
def get_script_dir():
|
||||||
|
"""获取脚本自身所在目录"""
|
||||||
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def get_config():
|
||||||
|
"""从 config.json 加载配置(优先脚本目录,其次当前目录)"""
|
||||||
|
# 优先从脚本同目录加载
|
||||||
|
cfg_path = os.path.join(get_script_dir(), "config.json")
|
||||||
|
try:
|
||||||
|
with open(cfg_path, "r", encoding="utf-8") as f:
|
||||||
|
cfg = json.load(f)
|
||||||
|
return cfg
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
# 其次从当前目录加载
|
||||||
|
try:
|
||||||
|
with open("config.json", "r", encoding="utf-8") as f:
|
||||||
|
cfg = json.load(f)
|
||||||
|
return cfg
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def make_headers():
|
||||||
|
"""构建 HTTP 请求头"""
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
token = AUTH_TOKEN
|
||||||
|
cfg = get_config()
|
||||||
|
if cfg:
|
||||||
|
token = cfg.get("auth_token", token)
|
||||||
|
if token:
|
||||||
|
headers["Authorization"] = f"Bearer {token}"
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def get_transit_url():
|
||||||
|
"""获取中转服务 URL"""
|
||||||
|
cfg = get_config()
|
||||||
|
if cfg and "transit_url" in cfg:
|
||||||
|
return cfg["transit_url"]
|
||||||
|
return TRANSIT_URL
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_id():
|
||||||
|
"""获取默认服务器 ID"""
|
||||||
|
cfg = get_config()
|
||||||
|
if cfg and "server_id" in cfg:
|
||||||
|
return cfg["server_id"]
|
||||||
|
return DEFAULT_SERVER_ID
|
||||||
|
|
||||||
|
|
||||||
|
def query(server_id, action, schema="", name="", timeout=60):
|
||||||
|
"""发送查询请求到中转服务"""
|
||||||
|
url = f"{get_transit_url()}/api/query"
|
||||||
|
payload = {
|
||||||
|
"server_id": server_id,
|
||||||
|
"action": action,
|
||||||
|
"schema": schema,
|
||||||
|
"name": name,
|
||||||
|
"timeout": timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = requests.post(url, json=payload, headers=make_headers(), timeout=timeout + 5)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return {"success": False, "error": "无法连接中转服务,请检查地址和端口"}
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return {"success": False, "error": "请求超时,Agent 可能未响应"}
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
return {"success": False, "error": f"HTTP 错误: {e}"}
|
||||||
|
|
||||||
|
|
||||||
|
def list_servers():
|
||||||
|
"""列出所有在线的 Agent 服务器"""
|
||||||
|
url = f"{get_transit_url()}/api/servers"
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, headers=make_headers(), timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def health_check():
|
||||||
|
"""检查中转服务健康状态"""
|
||||||
|
url = f"{get_transit_url()}/api/health"
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, timeout=5)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def print_result(result):
|
||||||
|
"""格式化输出结果"""
|
||||||
|
if result.get("success"):
|
||||||
|
data = result.get("data", "")
|
||||||
|
print(data)
|
||||||
|
else:
|
||||||
|
error = result.get("error", "未知错误")
|
||||||
|
print(f"❌ 错误: {error}")
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_mode():
|
||||||
|
"""交互式模式"""
|
||||||
|
print("=" * 50)
|
||||||
|
print(" Oracle Jump Query - AI Skill")
|
||||||
|
print(" 输入 help 查看命令,exit 退出")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Check transit server
|
||||||
|
health = health_check()
|
||||||
|
if "error" in health:
|
||||||
|
print(f"⚠️ 中转服务不可用: {health['error']}")
|
||||||
|
print("请检查 config.json 中的 transit_url 配置")
|
||||||
|
else:
|
||||||
|
print(f"✅ 中转服务正常 (在线 Agent: {health.get('agents', 0)})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = input("> ").strip()
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
print("\n再见!")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = line.split()
|
||||||
|
cmd = parts[0].lower()
|
||||||
|
|
||||||
|
if cmd == "exit" or cmd == "quit":
|
||||||
|
print("再见!")
|
||||||
|
break
|
||||||
|
|
||||||
|
elif cmd == "help":
|
||||||
|
print("""
|
||||||
|
可用命令:
|
||||||
|
analyze <schema> <procedure> - 分析存储过程(源码+依赖+表+触发器)
|
||||||
|
list <schema> - 列出 schema 下的所有存储过程
|
||||||
|
source <schema> <procedure> - 获取存储过程源码
|
||||||
|
deps <schema> <procedure> - 获取存储过程依赖
|
||||||
|
tables <schema> <procedure> - 获取相关表结构
|
||||||
|
servers - 列出在线 Agent
|
||||||
|
health - 检查中转服务状态
|
||||||
|
server <server_id> - 切换目标服务器
|
||||||
|
exit - 退出
|
||||||
|
""")
|
||||||
|
|
||||||
|
elif cmd == "servers":
|
||||||
|
result = list_servers()
|
||||||
|
if "error" in result:
|
||||||
|
print(f"❌ {result['error']}")
|
||||||
|
else:
|
||||||
|
servers = result.get("servers", [])
|
||||||
|
if not servers:
|
||||||
|
print("没有在线的 Agent")
|
||||||
|
else:
|
||||||
|
for s in servers:
|
||||||
|
print(f" 🟢 {s['server_id']}")
|
||||||
|
|
||||||
|
elif cmd == "health":
|
||||||
|
result = health_check()
|
||||||
|
if "error" in result:
|
||||||
|
print(f"❌ {result['error']}")
|
||||||
|
else:
|
||||||
|
print(f"✅ 中转服务正常 (在线 Agent: {result.get('agents', 0)})")
|
||||||
|
|
||||||
|
elif cmd == "server":
|
||||||
|
if len(parts) < 2:
|
||||||
|
print(f"当前服务器: {get_server_id()}")
|
||||||
|
else:
|
||||||
|
global DEFAULT_SERVER_ID
|
||||||
|
DEFAULT_SERVER_ID = parts[1]
|
||||||
|
print(f"切换到服务器: {parts[1]}")
|
||||||
|
|
||||||
|
elif cmd == "analyze":
|
||||||
|
if len(parts) < 3:
|
||||||
|
print("用法: analyze <schema> <procedure>")
|
||||||
|
continue
|
||||||
|
result = query(get_server_id(), "analyze_procedure", parts[1], parts[2], timeout=120)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "list":
|
||||||
|
if len(parts) < 2:
|
||||||
|
print("用法: list <schema>")
|
||||||
|
continue
|
||||||
|
result = query(get_server_id(), "list_procedures", parts[1], timeout=30)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "source":
|
||||||
|
if len(parts) < 3:
|
||||||
|
print("用法: source <schema> <procedure>")
|
||||||
|
continue
|
||||||
|
result = query(get_server_id(), "get_source", parts[1], parts[2], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "deps":
|
||||||
|
if len(parts) < 3:
|
||||||
|
print("用法: deps <schema> <procedure>")
|
||||||
|
continue
|
||||||
|
result = query(get_server_id(), "get_dependencies", parts[1], parts[2], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "tables":
|
||||||
|
if len(parts) < 3:
|
||||||
|
print("用法: tables <schema> <procedure>")
|
||||||
|
continue
|
||||||
|
result = query(get_server_id(), "get_tables", parts[1], parts[2], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"未知命令: {cmd},输入 help 查看帮助")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
interactive_mode()
|
||||||
|
return
|
||||||
|
|
||||||
|
cmd = sys.argv[1].lower()
|
||||||
|
|
||||||
|
if cmd == "servers":
|
||||||
|
result = list_servers()
|
||||||
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||||
|
|
||||||
|
elif cmd == "health":
|
||||||
|
result = health_check()
|
||||||
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||||
|
|
||||||
|
elif cmd == "analyze" and len(sys.argv) >= 4:
|
||||||
|
result = query(get_server_id(), "analyze_procedure", sys.argv[2], sys.argv[3], timeout=120)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "list" and len(sys.argv) >= 3:
|
||||||
|
result = query(get_server_id(), "list_procedures", sys.argv[2], timeout=30)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "source" and len(sys.argv) >= 4:
|
||||||
|
result = query(get_server_id(), "get_source", sys.argv[2], sys.argv[3], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "deps" and len(sys.argv) >= 4:
|
||||||
|
result = query(get_server_id(), "get_dependencies", sys.argv[2], sys.argv[3], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
elif cmd == "tables" and len(sys.argv) >= 4:
|
||||||
|
result = query(get_server_id(), "get_tables", sys.argv[2], sys.argv[3], timeout=60)
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(__doc__)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from scripts import oracle_skill as skill
|
||||||
|
|
||||||
|
|
||||||
|
class FakeResponse:
|
||||||
|
def __init__(self, chunks=None, payload=None, ok=True, status_code=200, headers=None):
|
||||||
|
self._chunks = chunks or []
|
||||||
|
self._payload = payload or {}
|
||||||
|
self.ok = ok
|
||||||
|
self.status_code = status_code
|
||||||
|
self.headers = headers or {}
|
||||||
|
self.text = ""
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc, tb):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
return self._payload
|
||||||
|
|
||||||
|
def iter_content(self, chunk_size=65536):
|
||||||
|
del chunk_size
|
||||||
|
return iter(self._chunks)
|
||||||
|
|
||||||
|
|
||||||
|
class AWRSkillTests(unittest.TestCase):
|
||||||
|
def test_default_transit_url_uses_https_domain(self):
|
||||||
|
self.assertEqual(skill.TRANSIT_URL, "https://ts.henlo.net")
|
||||||
|
|
||||||
|
def config(self):
|
||||||
|
return {
|
||||||
|
"access_token": "test-token",
|
||||||
|
"expires_at": "2999-01-01T00:00:00+08:00",
|
||||||
|
"transit_url": "http://transit.example",
|
||||||
|
"client_code": "WEIRUI",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_awr_status_uses_authenticated_dedicated_endpoint(self):
|
||||||
|
response = FakeResponse(payload={"success": True, "data": {"status": "ready"}})
|
||||||
|
with mock.patch.object(skill, "get_config", return_value=self.config()), mock.patch.object(
|
||||||
|
skill.requests, "get", return_value=response
|
||||||
|
) as get:
|
||||||
|
result = skill.awr_status("WEIRUI")
|
||||||
|
self.assertTrue(result["success"])
|
||||||
|
self.assertEqual(get.call_args.args[0], "http://transit.example/api/awr/status")
|
||||||
|
self.assertEqual(get.call_args.kwargs["params"], {"client_code": "WEIRUI"})
|
||||||
|
self.assertEqual(get.call_args.kwargs["headers"]["Authorization"], "Bearer test-token")
|
||||||
|
|
||||||
|
def test_awr_status_returns_schema_resolved_manual_dba_sql_when_permission_is_missing(self):
|
||||||
|
response = FakeResponse(
|
||||||
|
payload={
|
||||||
|
"success": True,
|
||||||
|
"client_code": "WEIRUI",
|
||||||
|
"data": {"status": "permission_required", "reason": "AWR export function is missing"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with mock.patch.object(skill, "get_config", return_value=self.config()), mock.patch.object(
|
||||||
|
skill.requests, "get", return_value=response
|
||||||
|
), mock.patch.object(skill, "query", return_value={"success": True, "data": {"schema": "bosnds3"}}):
|
||||||
|
result = skill.awr_status("WEIRUI")
|
||||||
|
self.assertTrue(result["dba_sql_required"])
|
||||||
|
self.assertEqual(result["agent_schema"], "BOSNDS3")
|
||||||
|
self.assertIn("GRANT EXECUTE ON SYS.HENLO_AWR_EXPORT TO BOSNDS3", result["dba_sql"])
|
||||||
|
self.assertIn("GRANT SELECT ON SYS.DBA_HIST_SNAPSHOT TO BOSNDS3", result["dba_sql"])
|
||||||
|
self.assertIn("IF R.OUTPUT IS NOT NULL THEN", result["dba_sql"])
|
||||||
|
self.assertIn("DBMS_LOB.WRITEAPPEND(L_HTML, 1, CHR(10))", result["dba_sql"])
|
||||||
|
self.assertNotIn("<AGENT_SCHEMA>", result["dba_sql"])
|
||||||
|
|
||||||
|
def test_awr_status_never_emits_placeholder_when_agent_schema_is_unavailable(self):
|
||||||
|
response = FakeResponse(
|
||||||
|
payload={"success": True, "data": {"status": "permission_required", "reason": "permission denied"}}
|
||||||
|
)
|
||||||
|
with mock.patch.object(skill, "get_config", return_value=self.config()), mock.patch.object(
|
||||||
|
skill.requests, "get", return_value=response
|
||||||
|
), mock.patch.object(skill, "query", return_value={"success": True, "data": {"schema": ""}}):
|
||||||
|
result = skill.awr_status("WEIRUI")
|
||||||
|
self.assertTrue(result["dba_sql_required"])
|
||||||
|
self.assertEqual(result["dba_sql"], "")
|
||||||
|
self.assertIn("oracle.schema", result["next_step"])
|
||||||
|
|
||||||
|
def test_download_preserves_exact_html_bytes(self):
|
||||||
|
content = "<html><body>未芮 AWR</body></html>".encode("utf-8")
|
||||||
|
response = FakeResponse(chunks=[content[:10], content[10:]], headers={"Content-Length": str(len(content))})
|
||||||
|
with tempfile.TemporaryDirectory() as root:
|
||||||
|
output = os.path.join(root, "awr.html")
|
||||||
|
with mock.patch.object(skill, "get_config", return_value=self.config()), mock.patch.object(
|
||||||
|
skill.requests, "get", return_value=response
|
||||||
|
) as get:
|
||||||
|
result = skill.download_awr_report("WEIRUI", "20260720", output)
|
||||||
|
self.assertTrue(result["success"], result)
|
||||||
|
with open(output, "rb") as f:
|
||||||
|
self.assertEqual(f.read(), content)
|
||||||
|
self.assertEqual(get.call_args.args[0], "http://transit.example/api/awr/download")
|
||||||
|
self.assertEqual(get.call_args.kwargs["params"], {"client_code": "WEIRUI", "date": "20260720"})
|
||||||
|
|
||||||
|
def test_download_rejects_invalid_date_before_network(self):
|
||||||
|
with mock.patch.object(skill, "get_config", return_value=self.config()), mock.patch.object(
|
||||||
|
skill.requests, "get"
|
||||||
|
) as get:
|
||||||
|
result = skill.download_awr_report("WEIRUI", "../secret")
|
||||||
|
self.assertFalse(result["success"])
|
||||||
|
get.assert_not_called()
|
||||||
|
|
||||||
|
def test_download_removes_partial_file_when_size_limit_is_exceeded(self):
|
||||||
|
response = FakeResponse(chunks=[b"123456"])
|
||||||
|
with tempfile.TemporaryDirectory() as root:
|
||||||
|
output = os.path.join(root, "awr.html")
|
||||||
|
with mock.patch.object(skill, "MAX_AWR_DOWNLOAD_BYTES", 5), mock.patch.object(
|
||||||
|
skill, "get_config", return_value=self.config()
|
||||||
|
), mock.patch.object(skill.requests, "get", return_value=response):
|
||||||
|
result = skill.download_awr_report("WEIRUI", "20260720", output)
|
||||||
|
self.assertFalse(result["success"])
|
||||||
|
self.assertFalse(os.path.exists(output))
|
||||||
|
self.assertFalse(os.path.exists(output + ".part"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user