316 lines
12 KiB
Markdown
316 lines
12 KiB
Markdown
# BOS 数据字典与系统地图
|
||
|
||
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
|
||
|
||
## 数据字典(AD_TABLE / AD_COLUMN / AD_REFBYTABLE)
|
||
|
||
### 单据编号定义(AD_SEQUENCE)
|
||
|
||
取单号函数为 `Get_SequenceNo('<CODE>', AD_CLIENT_ID)`。默认约定是 `CODE` 取单据 `DOCNO` 字段对应的 `AD_COLUMN.SEQUENCENAME` 值,CODE 的定义在 `AD_SEQUENCE` 表中;第二个参数取单据实际的 `AD_CLIENT_ID`。分析编号规则时,先定位单据及其 `DOCNO` 字段配置,再到 `AD_SEQUENCE` 核实对应定义,特殊业务按目标环境的实际配置和过程逻辑处理。查询 `AD_SEQUENCE` 前先确认表结构和字段含义,不假定关联字段或编号格式。取号只生成 SQL 示例,不通过查询通道调用函数。
|
||
|
||
### 通用查询规则
|
||
|
||
处理 BOS 业务单据(如零售单、采购单、调拨单、销售单等)时,必须遵循以下查询路径:
|
||
|
||
在品小二环境按中文名称或中文描述筛选时,遵守 [中文条件与字符集兼容](./query-and-business-rules.md#中文条件与字符集兼容) 规则:不要直接把中文写入 `LIKE` 或等值条件,改用 `UNISTR` Unicode 转义。直接中文字面量返回 0 行时,先用 `UNISTR` 形式重试。
|
||
|
||
#### 用户提供表名称时的对象解析顺序
|
||
|
||
用户给出一个表名称,且没有明确要求绕过 BOS 元数据、直接按 Oracle 物理对象查询时,不要立即对该名称执行 `describe` 或数据查询。先确认它是 BOS 逻辑表、BOS 实际表,还是未在 BOS 注册的数据库对象。
|
||
|
||
1. 先用用户给出的名称精确查询 `AD_TABLE`,同时返回英文名称 `NAME`、通常为中文的显示名称 `DESCRIPTION` 和 `REALTABLE_ID`。必要时再用 `LIKE` 扩大范围,不要把模糊命中的第一条直接当成目标。
|
||
|
||
```sql
|
||
SELECT t.ID,
|
||
t.NAME,
|
||
t.DESCRIPTION,
|
||
t.REALTABLE_ID,
|
||
rt.NAME AS REALTABLE_NAME,
|
||
rt.DESCRIPTION AS REALTABLE_DESCRIPTION
|
||
FROM AD_TABLE t
|
||
LEFT JOIN AD_TABLE rt ON rt.ID = t.REALTABLE_ID
|
||
WHERE UPPER(t.NAME) = UPPER('<表名称>')
|
||
OR t.DESCRIPTION = '<表名称>'
|
||
```
|
||
|
||
2. 查到 `AD_TABLE` 记录后,先向用户说明其 `DESCRIPTION` 和 `NAME`。若 `REALTABLE_ID` 非空,它表示该 BOS table 实际查询的表,应沿 `REALTABLE_ID -> AD_TABLE.ID` 使用目标记录的 `NAME` 继续查询结构或数据。原始名称可能只是 BOS 系统中的虚拟表,在 Oracle 数据库中并不存在;不要因为找不到同名物理表就判定对象不存在。若目标记录仍有 `REALTABLE_ID`,继续解析到最终实际表,并防止循环引用。
|
||
3. 查到 `AD_TABLE` 记录但 `REALTABLE_ID` 为空时,使用该记录的 `NAME` 作为物理表候选,再到数据库字典确认。
|
||
4. `AD_TABLE` 完全查不到时,该对象仍可能是未在 BOS 元数据中维护的数据库表。先查数据库表;查不到后再查普通视图,最后查物化视图。使用 `ALL_*` 字典时应返回 `OWNER`,避免把其他 schema 的同名对象误当成当前业务对象。
|
||
|
||
```sql
|
||
SELECT OWNER, TABLE_NAME
|
||
FROM ALL_TABLES t
|
||
WHERE t.TABLE_NAME = UPPER('<表名称>')
|
||
AND NOT EXISTS (
|
||
SELECT 1
|
||
FROM ALL_MVIEWS mv
|
||
WHERE mv.OWNER = t.OWNER
|
||
AND mv.MVIEW_NAME = t.TABLE_NAME
|
||
)
|
||
ORDER BY CASE WHEN OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') THEN 0 ELSE 1 END,
|
||
OWNER
|
||
```
|
||
|
||
```sql
|
||
SELECT OWNER, VIEW_NAME
|
||
FROM ALL_VIEWS
|
||
WHERE VIEW_NAME = UPPER('<表名称>')
|
||
ORDER BY CASE WHEN OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') THEN 0 ELSE 1 END,
|
||
OWNER
|
||
```
|
||
|
||
```sql
|
||
SELECT OWNER, MVIEW_NAME
|
||
FROM ALL_MVIEWS
|
||
WHERE MVIEW_NAME = UPPER('<表名称>')
|
||
ORDER BY CASE WHEN OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') THEN 0 ELSE 1 END,
|
||
OWNER
|
||
```
|
||
|
||
只有 `AD_TABLE`、数据库表、普通视图和物化视图都未命中时,才说明当前可见范围内未找到该对象。查询到 BOS 虚拟表时,业务含义和字段配置仍以原 `AD_TABLE` 记录为入口,物理结构与数据查询则以 `REALTABLE_ID` 最终指向的实际表为准。
|
||
|
||
**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 | 门店盘点日 |
|
||
|
||
|