Files

320 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BOS 数据字典与系统地图
> 本文件由 SKILL.md 整理拆分而来,保留原说明内容。
## 数据字典(AD_TABLE / AD_COLUMN / AD_REFBYTABLE)
### 单据编号定义(AD_SEQUENCE)
新建业务单据的表级 `MASK='MDQSV'`、`DOCNO` 生成器、字段级读写打印规则、`STATUS`/`ISACTIVE` 翻译器及选项组、提交人和提交时间配置,见 [BOS 新建业务单据表单默认配置](./query-and-business-rules.md#bos-新建业务单据表单默认配置)。表级规则与字段级规则分开维护;中文配置项对应的 `AD_COLUMN` 实际列名先查目标环境确认。
取单号函数为 `Get_SequenceNo('<CODE>', AD_CLIENT_ID)`。默认约定是 `CODE` 取单据 `DOCNO` 字段对应的 `AD_COLUMN.SEQUENCENAME` 值,CODE 的定义在 `AD_SEQUENCE` 表中;第二个参数取单据实际的 `AD_CLIENT_ID`。分析编号规则时,先定位单据及其 `DOCNO` 字段配置,再到 `AD_SEQUENCE` 核实对应定义,特殊业务按目标环境的实际配置和过程逻辑处理。查询 `AD_SEQUENCE` 前先确认表结构和字段含义,不假定关联字段或编号格式。取号只生成 SQL 示例,不通过查询通道调用函数。
### 通用查询规则
商品明细新增行为还需检查 `AD_TABLE.CLASSNAME`:`nds.schema.AttributeDetailSupportTableImpl` 表示支持在商品新增输入框输入条码录入。商品 ID、条码 ID、ASI ID 及 ASI 字段 `1100000000`(只在新增时可见)的配置约定见 [商品明细的条码新增配置](./query-and-business-rules.md#业务单据商品明细的条码新增配置)。明细新增功能以实际动作配置为准,不从主单据 `MASK` 推断。
处理 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 | 门店盘点日 |